win32_crash.patch

text/x-patch

Filename: win32_crash.patch
Type: text/x-patch
Part: 0
Message: Re: Re: Proposed Windows-specific change: Enable crash dumps (like core files)

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
File+
src/backend/main/main.c 8 0
src/backend/port/win32/crashdump.c 227 0
src/backend/port/win32/Makefile 1 0
src/include/port/win32.h 3 0
*** a/src/backend/main/main.c
--- b/src/backend/main/main.c
***************
*** 79,84 **** main(int argc, char *argv[])
--- 79,92 ----
  	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
  	 * already-initialized database.  We set them here so that they will be
*** a/src/backend/port/win32/Makefile
--- b/src/backend/port/win32/Makefile
***************
*** 12,17 **** subdir = src/backend/port/win32
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = timer.o socket.o signal.o security.o mingwcompat.o
  
  include $(top_srcdir)/src/backend/common.mk
--- 12,17 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = timer.o socket.o signal.o security.o mingwcompat.o crashdump.o
  
  include $(top_srcdir)/src/backend/common.mk
*** /dev/null
--- b/src/backend/port/win32/crashdump.c
***************
*** 0 ****
--- 1,227 ----
+ /*-------------------------------------------------------------------------
+  *
+  * 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 its self.
+  *
+  * 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.
+  *
+  *
+  * 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 LPAPI_VERSION (WINAPI *IMAGEHLPAPIVERSION)(void);
+ 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.
+  *
+  * This function is called in crash handler context. It can't trust that much
+  * of the backend is working.
+  *
+  * If NULL is returned, loading failed and the crash dump handler should
+  * not try to continue.
+  */
+ static HMODULE
+ loadDbgHelp(void)
+ {
+ 	HMODULE hDll = 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 whichever version of dbghelp.dll we can find.
+ 		 *
+ 		 * It is safe to call LoadLibrary with an unqualified name (thus
+ 		 * searching the PATH) only because the postmaster ensures that
+ 		 * backends run in a sensible environment with the datadir as
+ 		 * the working directory. It's usually unsafe to
+ 		 * LoadLibrary("unqualifiedname.dll")
+ 		 * because the user can run your program with a CWD containing
+ 		 * a malicious copy of "unqualifiedname.dll" thanks to the way
+ 		 * windows (rather insecurely) includes the CWD in the PATH by default.
+ 		 */
+ 		hDll = LoadLibrary("DBGHELP.DLL");
+ 	}
+ 
+ 	return hDll;
+ }
+ 
+ 
+ /*
+  * 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 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;
+ 		IMAGEHLPAPIVERSION pApiVersion = NULL;
+ 		MINIDUMPWRITEDUMP pDump = NULL;
+ 		LPAPI_VERSION version;
+ 		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 = loadDbgHelp();
+ 		if (hDll == NULL)
+ 		{
+ 			write_stderr("could not load dbghelp.dll, cannot write crashdump\n");
+ 			return EXCEPTION_CONTINUE_SEARCH;
+ 		}
+ 
+ 		pApiVersion = (IMAGEHLPAPIVERSION)GetProcAddress(hDll, "ImagehlpApiVersion");
+ 		pDump = (MINIDUMPWRITEDUMP)GetProcAddress(hDll, "MiniDumpWriteDump");
+ 
+ 		if (pApiVersion==NULL || 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.
+ 		 */
+ 		version = (*pApiVersion)();
+ 
+ 		dumpType = MiniDumpNormal | MiniDumpWithHandleData |
+ 			MiniDumpWithDataSegs;
+ 
+ 		if (version->MajorVersion >= 6)
+ 		{
+ 			/* Supported in versions higher than 5.1 */
+ 			dumpType |= MiniDumpWithIndirectlyReferencedMemory |
+ 				MiniDumpWithPrivateReadWriteMemory;
+ 		}
+ 		if (version->MajorVersion > 6 ||
+ 			(version->MajorVersion == 6 && version->MinorVersion > 1))
+ 		{
+ 			/* Supported in versions higher than 6.1 */
+ 			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);
+ }
*** a/src/include/port/win32.h
--- b/src/include/port/win32.h
***************
*** 303,308 **** extern int	pgwin32_is_service(void);
--- 303,311 ----
  /* 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);