Header And Logo

PostgreSQL
| The world's most advanced open source database.

crashdump.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * win32_crashdump.c
00004  *         Automatic crash dump creation for PostgreSQL on Windows
00005  *
00006  * The crashdump feature traps unhandled win32 exceptions produced by the
00007  * backend, and tries to produce a Windows MiniDump crash
00008  * dump for later debugging and analysis. The machine performing the dump
00009  * doesn't need any special debugging tools; the user only needs to send
00010  * the dump to somebody who has the same version of PostgreSQL and has debugging
00011  * tools.
00012  *
00013  * crashdump module originally by Craig Ringer <[email protected]>
00014  *
00015  * LIMITATIONS
00016  * ===========
00017  * This *won't* work in hard OOM situations or stack overflows.
00018  *
00019  * For those, it'd be necessary to take a much more complicated approach where
00020  * the handler switches to a new stack (if it can) and forks a helper process
00021  * to debug it self.
00022  *
00023  * POSSIBLE FUTURE WORK
00024  * ====================
00025  * For bonus points, the crash dump format permits embedding of user-supplied
00026  * data. If there's anything else that should always be supplied with a crash
00027  * dump (postgresql.conf? Last few lines of a log file?), it could potentially
00028  * be added, though at the cost of a greater chance of the crash dump failing.
00029  *
00030  *
00031  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00032  *
00033  * IDENTIFICATION
00034  *    src/backend/port/win32/crashdump.c
00035  *
00036  *-------------------------------------------------------------------------
00037  */
00038 
00039 #include "postgres.h"
00040 
00041 #define WIN32_LEAN_AND_MEAN
00042 #include <windows.h>
00043 #include <string.h>
00044 #include <dbghelp.h>
00045 
00046 /*
00047  * Much of the following code is based on CodeProject and MSDN examples,
00048  * particularly
00049  * http://www.codeproject.com/KB/debug/postmortemdebug_standalone1.aspx
00050  *
00051  * Useful MSDN articles:
00052  *
00053  * http://msdn.microsoft.com/en-us/library/ff805116(v=VS.85).aspx
00054  * http://msdn.microsoft.com/en-us/library/ms679294(VS.85).aspx
00055  *
00056  * Other useful articles on working with minidumps:
00057  * http://www.debuginfo.com/articles/effminidumps.html
00058  */
00059 
00060 typedef BOOL (WINAPI * MINIDUMPWRITEDUMP) (HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
00061                         CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
00062                      CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
00063                            CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
00064 );
00065 
00066 
00067 /*
00068  * This function is the exception handler passed to SetUnhandledExceptionFilter.
00069  * It's invoked only if there's an unhandled exception. The handler will use
00070  * dbghelp.dll to generate a crash dump, then resume the normal unhandled
00071  * exception process, which will generally exit with an error message from
00072  * the runtime.
00073  *
00074  * This function is run under the unhandled exception handler, effectively
00075  * in a crash context, so it should be careful with memory and avoid using
00076  * any PostgreSQL functions.
00077  */
00078 static LONG WINAPI
00079 crashDumpHandler(struct _EXCEPTION_POINTERS * pExceptionInfo)
00080 {
00081     /*
00082      * We only write crash dumps if the "crashdumps" directory within the
00083      * postgres data directory exists.
00084      */
00085     DWORD       attribs = GetFileAttributesA("crashdumps");
00086 
00087     if (attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY))
00088     {
00089         /* 'crashdumps' exists and is a directory. Try to write a dump' */
00090         HMODULE     hDll = NULL;
00091         MINIDUMPWRITEDUMP pDump = NULL;
00092         MINIDUMP_TYPE dumpType;
00093         char        dumpPath[_MAX_PATH];
00094         HANDLE      selfProcHandle = GetCurrentProcess();
00095         DWORD       selfPid = GetProcessId(selfProcHandle);
00096         HANDLE      dumpFile;
00097         DWORD       systemTicks;
00098         struct _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
00099 
00100         ExInfo.ThreadId = GetCurrentThreadId();
00101         ExInfo.ExceptionPointers = pExceptionInfo;
00102         ExInfo.ClientPointers = FALSE;
00103 
00104         /* Load the dbghelp.dll library and functions */
00105         hDll = LoadLibrary("dbghelp.dll");
00106         if (hDll == NULL)
00107         {
00108             write_stderr("could not load dbghelp.dll, cannot write crash dump\n");
00109             return EXCEPTION_CONTINUE_SEARCH;
00110         }
00111 
00112         pDump = (MINIDUMPWRITEDUMP) GetProcAddress(hDll, "MiniDumpWriteDump");
00113 
00114         if (pDump == NULL)
00115         {
00116             write_stderr("could not load required functions in dbghelp.dll, cannot write crash dump\n");
00117             return EXCEPTION_CONTINUE_SEARCH;
00118         }
00119 
00120         /*
00121          * Dump as much as we can, except shared memory, code segments, and
00122          * memory mapped files. Exactly what we can dump depends on the
00123          * version of dbghelp.dll, see:
00124          * http://msdn.microsoft.com/en-us/library/ms680519(v=VS.85).aspx
00125          */
00126         dumpType = MiniDumpNormal | MiniDumpWithHandleData |
00127             MiniDumpWithDataSegs;
00128 
00129         if (GetProcAddress(hDll, "EnumDirTree") != NULL)
00130         {
00131             /* If this function exists, we have version 5.2 or newer */
00132             dumpType |= MiniDumpWithIndirectlyReferencedMemory |
00133                 MiniDumpWithPrivateReadWriteMemory;
00134         }
00135 
00136         systemTicks = GetTickCount();
00137         snprintf(dumpPath, _MAX_PATH,
00138                  "crashdumps\\postgres-pid%0i-%0i.mdmp",
00139                  (int) selfPid, (int) systemTicks);
00140         dumpPath[_MAX_PATH - 1] = '\0';
00141 
00142         dumpFile = CreateFile(dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE,
00143                               NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
00144                               NULL);
00145         if (dumpFile == INVALID_HANDLE_VALUE)
00146         {
00147             write_stderr("could not open crash dump file \"%s\" for writing: error code %lu\n",
00148                          dumpPath, GetLastError());
00149             return EXCEPTION_CONTINUE_SEARCH;
00150         }
00151 
00152         if ((*pDump) (selfProcHandle, selfPid, dumpFile, dumpType, &ExInfo,
00153                       NULL, NULL))
00154             write_stderr("wrote crash dump to file \"%s\"\n", dumpPath);
00155         else
00156             write_stderr("could not write crash dump to file \"%s\": error code %lu\n",
00157                          dumpPath, GetLastError());
00158 
00159         CloseHandle(dumpFile);
00160     }
00161 
00162     return EXCEPTION_CONTINUE_SEARCH;
00163 }
00164 
00165 
00166 void
00167 pgwin32_install_crashdump_handler(void)
00168 {
00169     SetUnhandledExceptionFilter(crashDumpHandler);
00170 }