On Thu, Dec 16, 2010 at 14:01, Magnus Hagander <mag...@hagander.net> wrote:
> On Thu, Dec 16, 2010 at 09:21, Craig Ringer <cr...@postnewspapers.com.au> 
> wrote:
>> Hi all
>>
>> Updated patch attached. This one avoids the inline function stuff and
>> instead just decides whether to compile unix_crashdump.c or
>> win32_crashdump.c based on build system tests. It passes "make check" on
>> nix and tests on win32, both with and without the "crashdumps" directory
>> existing. The inadvertent duplication of the functon prototype is fixed.
>
> I've updated this patch in quite a few ways, and you can find the
> latest on my github (will attach when I'm done with cleanups). Things
> I've changed so far:

Found another problem in it: when running with an older version of
dbghelp.dll (which I was), it simply didn't work. We need to grab the
version of dbghelp.dll at runtime and pick which things we're going to
dump based on that.

The attached version of the patch does that. Can you please test that
it still generates the full dump on your system, which supposedly has
a much more modern version of dbghelp.dll than mine?


-- 
 Magnus Hagander
 Me: http://www.hagander.net/
 Work: http://www.redpill-linpro.com/
*** 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 <ring...@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);
  
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to