On 4/10/2010 8:27 PM, Heikki Linnakangas wrote:
On 04.10.2010 15:21, Craig Ringer wrote:
Thinking about it, a possibly better alternative is to ship it as a
library as is done with the pl/pgsql debugger, and use (I think)
shared_preload_libraries to load it when desired. That way there's zero
cost if disabled, though a somewhat higher cost if enabled.
Hmm. That'll let me put a test version together that'll work with 9.0 as
well, so that seems like a no-brainer really, it's just a better way to
do it. Time for a Pg-on-windows build, yay.
If it's not a lot of code, it's better to have it built-in always.
Loading extra libraries in debug-mode could lead to heisenbugs.
It turns out that the basic minidumps are small (21kb here) and very
fast to generate. It may well be worth leaving it enabled all the time
after all. I just need to time how long the handler registration takes -
though as LOAD of the current handler implemented as a contrib module
takes 2.1ms, and LOAD of an module with an empty _PG_init() also takes
2.1ms, I'm guessing "not long".
I've attached an early version for people to play with if anyone's
interested. It currently contains a bunch of elog() messages to report
on its progress - though I'm not at all sure elog() should be used in
the final version given that Pg is crashing at the time the handler is
called and there's no guarantee elog() is safe to call. It also doesn't
offer any way to set the dump type yet, it's always the minimal dump
with exception info, registers and stack only. Finally, a "crashme"
function is included to trigger a reliable unhandled exception on
demand, for initial testing.
Usage with Pg built from source on Windows:
- Drop crashdump.c and the Makefile in contrib/crashdump
- Run build.bat and install.bat
- Create a "crashdumps" directory inside your datadir, and make
sure the server has read/write/create permission on it.
- add 'crashdump' to shared_preload_libraries in postgresql.conf
- Start the server as normal
- Start psql and issue:
-- CREATE OR REPLACE FUNCTION crashdump_crashme() RETURNS void
AS '$libdir/crashdump','crashdump_crashme' LANGUAGE C;
-- SELECT crashdump_crashme();
Your backend should crash. In the error log (and, depending on how the
buffering works out, maybe in psql) you should see:
WARNING: loading dll
WARNING: loading dll try 1, 00000000
WARNING: loading dll try 2, 73880000
WARNING: pdump: 738C70D8
WARNING: Generating dumppath
WARNING: Generated dumppath
WARNING: dumping to path: crashdumps\backend.dmp
WARNING: outfile: 000000B0
WARNING: about to dump
NOTICE: crashdump: wrote crash dump to crashdumps\postgres-4341.mdmp
Once you have the dump file, you can fire up windbg from the Debugging
Tools for Windows and use File->Open Crash Dump to open it. The .excr
command will report what the exception that caused the crash was,
producing output like this:
0:000> .ecxr
eax=00000000 ebx=00000000 ecx=02a1e290 edx=73831014 esi=02a1e188 edi=00c3f798
eip=738313b2 esp=00c3f724 ebp=02a1e280 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
crashdump!crashdump_crashme+0x2:
738313b2 c70001000000 mov dword ptr [eax],1 ds:0023:00000000=????????
and if possible opening the postgresql source file the crash happened in
to the appropriate line:
Datum
crashdump_crashme(PG_FUNCTION_ARGS)
{
int * ptr = NULL;
*ptr = 1; <--- highlighted
return NULL;
}
If you're using Visual Studio Professional (not the free Express
edition, unfortunately) you should also be able to debug the crash dump
that way. I don't have it to test with.
--
Craig Ringer
Tech-related writing at http://soapyfrogs.blogspot.com/
/*-------------------------------------------------------------------------
*
* 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 );
}
MODULE_big = crashdump
OBJS = crashdump.o
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/crashdump
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers