/*------------------------------------------------------------------------- * * crashdump.c * Automatic crash dump creation for PostgreSQL on Windows * * The crashdump module traps unhandled exceptions produced by the backend * the module is loaded in, 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 its self. That's in the too hard basket as far as I'm concerned; * this approach will get 90% of the results with 10% of the work. * * POSSIBLE FUTURE WORK: * ===================== * For bonus points, the crash dump format permits embedding of user-supplied data. * If there's anything else (postgresql.conf? Last few lines of a log file?) that * should always be supplied with a crash dump, it could potentially be added, * though at the cost of a greater chance of the crash dump failing. Again, * I'm not going to tackle that, but thought it worth mentioning in case * someone wants it down the track. * *------------------------------------------------------------------------- */ #include "postgres.h" #include "fmgr.h" #define VC_EXTRALEAN #include #include #include 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/ff805116(v=VS.85).aspx * 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 * * 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 ); /* * 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]; if (GetModuleFileName( NULL, dbgHelpPath, _MAX_PATH )) { char *slash = strrchr( dbgHelpPath, '\\' ); if (slash) { strcpy( slash+1, "DBGHELP.DLL" ); hDll = LoadLibrary( dbgHelpPath ); } } if (hDll==NULL) { // Load any version we can hDll = LoadLibrary( "DBGHELP.DLL" ); } if (hDll!=NULL) { pDump = (MINIDUMPWRITEDUMP)GetProcAddress( hDll, "MiniDumpWriteDump" ); } 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]; // Dump pretty much everything except shared memory, code segments, and memory mapped files MINIDUMP_TYPE dumpType = MiniDumpNormal|\ MiniDumpWithIndirectlyReferencedMemory|\ MiniDumpWithHandleData|\ MiniDumpWithThreadInfo|\ MiniDumpWithPrivateReadWriteMemory| MiniDumpWithDataSegs; HANDLE selfProcHandle = GetCurrentProcess(); DWORD selfPid = GetProcessId(selfProcHandle); HANDLE dumpFile; struct _MINIDUMP_EXCEPTION_INFORMATION ExInfo; ExInfo.ThreadId = GetCurrentThreadId(); ExInfo.ExceptionPointers = pExceptionInfo; ExInfo.ClientPointers = FALSE; pDump = loadDbgHelp(); if (pDump==NULL) return EXCEPTION_CONTINUE_SEARCH; snprintf(&dumpPath[0], _MAX_PATH, "crashdumps\\postgres-%i.mdmp", selfPid); 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) { elog(WARNING, "crashdump: Unable to open dump file %s for writing (win32 error %i)", &dumpPath[0], GetLastError()); elog(DEBUG1, "crashdump: is there a 'crashdump' directory within the data dir, and is it writable by the postgres user?"); return EXCEPTION_CONTINUE_SEARCH; } if( (*pDump)( selfProcHandle, selfPid, dumpFile, dumpType, &ExInfo, NULL, NULL ) ) { elog(WARNING,"crashdump: wrote crash dump to %s", &dumpPath[0]); } else { elog(WARNING,"crashdump: failed to write dump file to %s (win32 error %i)", &dumpPath[0], GetLastError()); } 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 ); }