Skip to content
Snippets Groups Projects
plugin_debugger.c 48.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • /**********************************************************************
    
     * plugin_debugger.c	- Language-independent parts of debugger
    
    Christoph Berg's avatar
    Christoph Berg committed
     * Copyright (c) 2004-2024 EnterpriseDB Corporation. All Rights Reserved.
    
     * Licensed under the Artistic License v2.0, see
     *		https://opensource.org/licenses/artistic-license-2.0
    
     * for full details
     *
     **********************************************************************/
    
    
    #include <stdio.h>
    #include <stdarg.h>
    #include <unistd.h>
    #include <errno.h>
    #include <setjmp.h>
    #include <signal.h>
    
    #ifdef WIN32
    	#include<winsock2.h>
    #else
    
    	#include <sys/socket.h>
    	#include <arpa/inet.h>
    #endif
    
    
    #include "lib/stringinfo.h"
    #include "catalog/pg_proc.h"
    
    #include "catalog/pg_type.h"
    
    #if (PG_VERSION_NUM >= 130000)
    #include "common/hashfn.h"
    #endif
    
    #include "parser/parser.h"
    #include "parser/parse_func.h"
    #include "globalbp.h"
    #include "storage/proc.h"							/* For MyProc		   */
    
    #include "storage/procarray.h"						/* For BackendPidGetProc */
    
    #include "utils/array.h"
    #include "utils/builtins.h"
    #include "utils/syscache.h"
    
    #include "miscadmin.h"
    
    #include "dbgcomm.h"
    
    /* Include header for GETSTRUCT */
    #if (PG_VERSION_NUM >= 90300)
    #include "access/htup_details.h"
    #endif
    
    
    #define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
    
    
    #define	TARGET_PROTO_VERSION	"1.1"
    
    
    /**********************************************************************
     * Type and structure definitions
     **********************************************************************/
    
    
     *	This enum defines the different ways that we can connect to the
     *  debugger proxy.
    
     *
     *		CONNECT_AS_SERVER means that we create a socket, bind an address to
     *		to that socket, send a NOTICE to our client application, and wait for
    
     *		a debugger proxy to attach to us.  That's what happens when	your
     *		client application sets a local breakpoint and can handle the
    
     *		NOTICE that we send.
     *
     *		CONNECT_AS_CLIENT means that a proxy has already created a socket
     *		and is waiting for a target (that's us) to connect to it. We do
     *		this kind of connection stuff when a debugger client sets a global
     *		breakpoint and we happen to blunder into that breakpoint.
     *
     *		CONNECT_UNKNOWN indicates a problem, we shouldn't ever see this.
     */
    
    typedef enum
    {
    	CONNECT_AS_SERVER, 	/* Open a server socket and wait for a proxy to connect to us	*/
    	CONNECT_AS_CLIENT,	/* Connect to a waiting proxy (global breakpoints do this)		*/
    	CONNECT_UNKNOWN		/* Must already be connected 									*/
    } eConnectType;
    
    
    /* Global breakpoint data. */
    typedef struct
    {
    #if (PG_VERSION_NUM >= 90600)
    	int		tranche_id;
    	LWLock	lock;
    #else
    	LWLockId	lockid;
    #endif
    } GlobalBreakpointData;
    
    
    /**********************************************************************
     * Local (static) variables
     **********************************************************************/
    
    
    
    static debugger_language_t *debugger_languages[] = {
    	&plpgsql_debugger_lang,
    #ifdef INCLUDE_PACKAGE_SUPPORT
    	&spl_debugger_lang,
    #endif
    	NULL
    };
    
    
    #if (PG_VERSION_NUM >= 150000)
    static shmem_request_hook_type prev_shmem_request_hook = NULL;
    #endif
    
    
    /**********************************************************************
     * Function declarations
     **********************************************************************/
    
    void _PG_init( void );				/* initialize this module when we are dynamically loaded	*/
    
    /**********************************************************************
     * Local (hidden) function prototypes
     **********************************************************************/
    
    
    #if (PG_VERSION_NUM >= 150000)
    static void			 pldebugger_shmem_request( void );
    #endif
    
    
    static void        * writen( int peer, void * src, size_t len );
    static bool 		 connectAsServer( void );
    static bool 		 connectAsClient( Breakpoint * breakpoint );
    static bool 		 handle_socket_error(void);
    static bool 		 parseBreakpoint( Oid * funcOID, int * lineNumber, char * breakpointString );
    static bool 		 addLocalBreakpoint( Oid funcOID, int lineNo );
    static void			 reserveBreakpoints( void );
    
    static debugger_language_t *language_of_frame(ErrorContextCallback *frame);
    
    static char * findSource( Oid oid, HeapTuple * tup );
    
    
    static void do_deposit(ErrorContextCallback *frame, debugger_language_t *lang,
    					   char *command);
    static void send_breakpoints(Oid funcOid);
    static void send_stack(void);
    static void select_frame(int frameNo, ErrorContextCallback **frame_p, debugger_language_t **lang_p);
    
    
    
    /**********************************************************************
     * Function definitions
     **********************************************************************/
    
    void _PG_init( void )
    {
    
    	int i;
    
    	/* Initialize all the per-language hooks. */
    	for (i = 0; debugger_languages[i] != NULL; i++)
    		debugger_languages[i]->initialize();
    
    #if (PG_VERSION_NUM >= 150000)
    	prev_shmem_request_hook = shmem_request_hook;
    	shmem_request_hook = pldebugger_shmem_request;
    #else
        reserveBreakpoints();
        dbgcomm_reserve();
    #endif
    }
    
    #if (PG_VERSION_NUM >= 150000)
    static void pldebugger_shmem_request( void )
    {
    	if (prev_shmem_request_hook)
    		prev_shmem_request_hook();
    
    
    #endif
    
     * CREATE OR REPLACE FUNCTION pldbg_oid_debug( functionOID OID ) RETURNS INTEGER AS 'pldbg_oid_debug' LANGUAGE C;
    
    Christoph Berg's avatar
    Christoph Berg committed
    PGDLLEXPORT Datum pldbg_oid_debug(PG_FUNCTION_ARGS);
    
    Heikki Linnakangas's avatar
    Heikki Linnakangas committed
    	Oid			funcOid;
    	HeapTuple	tuple;
    	Oid			userid;
    
    
    	if(( funcOid = PG_GETARG_OID( 0 )) == InvalidOid )
    		ereport( ERROR, ( errcode( ERRCODE_UNDEFINED_FUNCTION ), errmsg( "no target specified" )));
    
    
    	/* get the owner of the function */
    	tuple = SearchSysCache(PROCOID,
    				   ObjectIdGetDatum(funcOid),
    				   0, 0, 0);
    	if (!HeapTupleIsValid(tuple))
    		elog(ERROR, "cache lookup failed for function %u",
    			 funcOid);
    	userid = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
    	ReleaseSysCache(tuple);
    
    	if( !superuser() && (GetUserId() != userid))
    		ereport( ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg( "must be owner or superuser to create a breakpoint" )));
    
    
    	addLocalBreakpoint( funcOid, -1 );
    
    	PG_RETURN_INT32( 0 );
    }
    
    /*
     * ---------------------------------------------------------------------
     * readn()
     *
    
     *	This function reads exactly 'len' bytes from the given socket or it
     *	throws an error.  readn() will hang until the proper number of bytes
    
     *	Note: dst must point to a buffer large enough to hold at least 'len'
    
     *	bytes.  readn() returns dst (for convenience).
     */
    
    static void * readn( int peer, void * dst, size_t len )
    {
    	size_t	bytesRemaining = len;
    	char  * buffer         = (char *)dst;
    
    	while( bytesRemaining > 0 )
    	{
    		ssize_t bytesRead = recv( peer, buffer, bytesRemaining, 0 );
    
    		if( bytesRead <= 0 && errno != EINTR )
    			handle_socket_error();
    
    
    		/* Ignore if we didn't receive anything. */
    		if ( bytesRead > 0 )
    		{
    			bytesRemaining -= bytesRead;
    			buffer += bytesRead;
    		}
    
    	}
    
    	return( dst );
    }
    
    /*
     * ---------------------------------------------------------------------
     * readUInt32()
     *
     *	Reads a 32-bit unsigned value from the server (and returns it in the host's
     *	byte ordering)
     */
    
    static uint32 readUInt32( int channel )
    {
    	uint32	netVal;
    
    	readn( channel, &netVal, sizeof( netVal ));
    
    	return( ntohl( netVal ));
    }
    
    /*
     * ---------------------------------------------------------------------
     * dbg_read_str()
     *
     *	This function reads a counted string from the given stream
    
     *	Returns a palloc'd, null-terminated string.
    
     *	NOTE: the server-side of the debugger uses this function to read a
    
    Heikki Linnakangas's avatar
    Heikki Linnakangas committed
     * ---------------------------------------------------------------------
    
     *	This function writes exactly 'len' bytes to the given socket or it
    
     *  	throws an error.  writen() will hang until the proper number of bytes
     *	have been written (or an error occurs).
     */
    
    static void * writen( int peer, void * src, size_t len )
    {
    	size_t	bytesRemaining = len;
    	char  * buffer         = (char *)src;
    
    	while( bytesRemaining > 0 )
    	{
    		ssize_t bytesWritten;
    
    		if(( bytesWritten = send( peer, buffer, bytesRemaining, 0 )) <= 0 )
    			handle_socket_error();
    
    		bytesRemaining -= bytesWritten;
    		buffer         += bytesWritten;
    	}
    
    	return( src );
    }
    
    
    /*
     * ---------------------------------------------------------------------
     * sendUInt32()
     *
     *	This function sends a uint32 value (val) to the debugger server.
     */
    
    static void sendUInt32( int channel, uint32 val )
    {
    	uint32	netVal = htonl( val );
    
    	writen( channel, &netVal, sizeof( netVal ));
    }
    
    /*
     * ---------------------------------------------------------------------
     * dbg_send()
     *
     *	This function writes a formatted, counted string to the
     *	given stream.  The argument list for this function is identical to
    
     *	the argument list for the fprintf() function - you provide a socket,
    
     *	a format string, and then some number of arguments whose meanings
    
     *	are defined by the format string.
    
     *
     *	NOTE:  the server-side of the debugger uses this function to send
     *		   data to the client side.  If the connection drops, dbg_send()
    
     *		   will longjmp() back to the debugger top-level so that the
    
    	StringInfoData	result;
    	char		   *data;
    	size_t			remaining;
    
    	if( !sock )
    		return;
    
    	initStringInfo(&result);
    
    	for (;;)
    
    #if (PG_VERSION_NUM >= 90400)
    		int			needed;
    
    		va_start(args, fmt);
    		needed = appendStringInfoVA(&result, fmt, args);
    		va_end(args);
    
    		if (needed == 0)
    			break;
    
    		enlargeStringInfo(&result, needed);
    #else
    
    		va_start(args, fmt);
    		success = appendStringInfoVA(&result, fmt, args);
    		va_end(args);
    
    		enlargeStringInfo(&result, result.maxlen);
    
    	data = result.data;
    	remaining = strlen(data);
    
    	sendUInt32(sock, remaining);
    
    	while( remaining > 0 )
    	{
    		int written = send( sock, data, remaining, 0 );
    
    			handle_socket_error();
    			continue;
    
    		remaining -= written;
    		data      += written;
    
    
    /*
     * ---------------------------------------------------------------------
     * dbg_send_src()
     *
     *	dbg_send_src() sends the source code for a function to the client.
     *
     *  The client caches the source code that we send it and uses xmin/cmin
     *  to ensure the validity of the cache.
     */
    
    static void dbg_send_src( char * command  )
    {
    
    	HeapTuple			tup;
    	char				*procSrc;
    
    	Oid					targetOid = InvalidOid;  /* Initialize to keep compiler happy */
    
    	targetOid = atoi( command + 2 );
    
    	/* Find the source code for this function */
    	procSrc = findSource( targetOid, &tup );
    
    	/* Found it - now send the source to the client */
    
    	dbg_send( "%s", procSrc );
    
    	/* Release the process tuple and send a footer to the client so he knows we're finished */
    
    
    	ReleaseSysCache( tup );
    
    /*
     * ---------------------------------------------------------------------
     * findSource()
     *
     *	This function locates and returns a pointer to a null-terminated string
     *	that contains the source code for the given function (identified by its
     *	OID).
     *
     *	In addition to returning a pointer to the requested source code, this
    
     *	function sets *tup to point to a HeapTuple (that you must release when
    
    Heikki Linnakangas's avatar
    Heikki Linnakangas committed
     *	you are finished with it).
    
    static char * findSource( Oid oid, HeapTuple * tup )
    
    {
    	bool	isNull;
    
    	*tup = SearchSysCache( PROCOID, ObjectIdGetDatum( oid ), 0, 0, 0 );
    
    	if(!HeapTupleIsValid( *tup ))
    		elog( ERROR, "pldebugger: cache lookup for proc %u failed", oid );
    
    	return( DatumGetCString( DirectFunctionCall1( textout, SysCacheGetAttr( PROCOID, *tup, Anum_pg_proc_prosrc, &isNull ))));
    }
    
    
    /*
     * ---------------------------------------------------------------------
     * attach_to_proxy()
     *
     *	This function creates a connection to the debugger client (via the
     *  proxy process). attach_to_proxy() will hang the PostgreSQL backend
     *  until the debugger client completes the connection.
     *
    
     *	We start by asking the TCP/IP stack to allocate an unused port, then we
    
     *	extract the port number from the resulting socket, send the port number to
     *	the client application (by raising a NOTICE), and finally, we wait for the
     *	client to connect.
     *
     *	We assume that the client application knows the IP address of the PostgreSQL
    
     *	backend process - if that turns out to be a poor assumption, we can include
    
     *	the IP address in the notification string that we send to the client application.
     */
    
    
    bool attach_to_proxy( Breakpoint * breakpoint )
    
    	if( per_session_ctx.client_w )
    
    	{
    		/* We're already connected to a live proxy, just go home */
    		return( TRUE );
    	}
    
    	if( breakpoint == NULL )
    	{
    
    		 * No breakpoint - that implies that we're 'stepping into'.
    		 * We had better already have a connection to a proxy here
    		 * (how could we be 'stepping into' if we aren't connected
    		 * to a proxy?)
    		 */
    		return( FALSE );
    	}
    
    	/*
    	 * When a networking error is detected, we longjmp() to the client_lost
    	 * error handler - that normally points to a location inside of dbg_newstmt()
    
    	 * but we want to handle any network errors that arise while we are
    
    	 * setting up a link to the proxy.  So, we save the original client_lost
    	 * error handler context and push our own context on to the stack.
    	 */
    
    	save = client_lost;
    
    	if( sigsetjmp( client_lost.m_savepoint, 1 ) != 0 )
    	{
    		client_lost = save;
    		return( FALSE );
    	}
    
    
    	if( breakpoint->data.proxyPort == -1 )
    
    	{
    		/*
    		 * proxyPort == -1 implies that this is a local breakpoint,
    		 * create a server socket and wait for the proxy to contact
    		 * us.
    		 */
    		result = connectAsServer();
    	}
    	else
    	{
    		/*
    		 * proxyPort != -1 implies that this is a global breakpoint,
    		 * a debugger proxy is already waiting for us at the given
    		 * port (on this host), connect to that proxy.
    		 */
    
    		result = connectAsClient( breakpoint );
    	}
    
    	/*
    	 * Now restore the original error handler context so that
    	 * dbg_newstmt() can handle any future network errors.
    	 */
    
    	client_lost = save;
    	return( result );
    }
    
    /*
     * ---------------------------------------------------------------------
     * connectAsServer()
     *
     *	This function creates a socket, asks the TCP/IP stack to bind it to
     *	an unused port, and then waits for a debugger proxy to connect to
    
     *	that port.  We send a NOTICE to our client process (on the other
    
     *	end of the fe/be connection) to let the client know that it should
    
     *	fire up a debugger and attach to that port (the NOTICE includes
    
     *	the port number)
     */
    
    static bool connectAsServer( void )
    {
    
    	client_sock = dbgcomm_listen_for_proxy();
    	if (client_sock < 0)
    
    		per_session_ctx.client_w = per_session_ctx.client_r = 0;
    
    		return( FALSE );
    
    		per_session_ctx.client_w = client_sock;
    		per_session_ctx.client_r = client_sock;
    
    	}
    }
    
    /*
     * ---------------------------------------------------------------------
     * connectAsClient()
     *
     *	This function connects to a waiting proxy process over the given
     *  port. We got the port number from a global breakpoint (the proxy
    
     *	stores it's port number in the breakpoint so we'll know how to
    
     *	find that proxy).
     */
    
    static bool connectAsClient( Breakpoint * breakpoint )
    {
    	int					 proxySocket;
    
    
    	proxySocket = dbgcomm_connect_to_proxy(breakpoint->data.proxyPort);
    
    	if (proxySocket < 0 )
    
    		/* dbgcomm_connect_to_proxy already logged the reason */
    		return false;
    
    		per_session_ctx.client_w = proxySocket;
    		per_session_ctx.client_r = proxySocket;
    
    		BreakpointBusySession( breakpoint->data.proxyPid );
    		return true;
    	}
    
    /*
     * ---------------------------------------------------------------------
     * parseBreakpoint()
    
     *	Given a string that formatted like "funcOID:linenumber",
     *	this function parses out the components and returns them to the
     *	caller.  If the string is well-formatted, this function returns
    
    static bool parseBreakpoint( Oid * funcOID, int * lineNumber, char * breakpointString )
    
    	n = sscanf(breakpointString, "%d:%d", &a, &b);
    	if (n == 2)
    
    Heikki Linnakangas's avatar
    Heikki Linnakangas committed
     * ---------------------------------------------------------------------
    
     *	This function adds a local breakpoint for the given function and
    
     *	line number
     */
    
    static bool addLocalBreakpoint( Oid funcOID, int lineNo )
    {
    	Breakpoint breakpoint;
    
    	breakpoint.key.databaseId = MyProc->databaseId;
    	breakpoint.key.functionId = funcOID;
    	breakpoint.key.lineNumber = lineNo;
    	breakpoint.key.targetPid  = MyProc->pid;
    	breakpoint.data.isTmp     = FALSE;
    	breakpoint.data.proxyPort = -1;
    	breakpoint.data.proxyPid  = -1;
    
    	return( BreakpointInsert( BP_LOCAL, &breakpoint.key, &breakpoint.data ));
    }
    
    /*
    
    Heikki Linnakangas's avatar
    Heikki Linnakangas committed
     * ---------------------------------------------------------------------
    
     *	The debugger client can set a local breakpoint at a given
    
     *	function/procedure and line number by calling	this function
     *  (through the debugger proxy process).
     */
    
    
    	 *  Format is 'b funcOID:lineNumber'
    	 */
    	int			  lineNo;
    	Oid			  funcOID;
    
    	if( parseBreakpoint( &funcOID, &lineNo, command + 2 ))
    	{
    		if( addLocalBreakpoint( funcOID, lineNo ))
    
    Heikki Linnakangas's avatar
    Heikki Linnakangas committed
     * ---------------------------------------------------------------------
    
     * clearBreakpoint()
     *
     *	This function deletes the breakpoint at the package,
     *	function/procedure, and line number indicated by the
     *	given command.
     *
     *	For now, we maintain our own private list of breakpoints -
     *	later, we'll use the same list managed by the CREATE/
     *	DROP BREAKPOINT commands.
     */
    
    
    	 *  Format is 'f funcOID:lineNumber'
    	 */
    	int			  lineNo;
    	Oid			  funcOID;
    
    	if( parseBreakpoint( &funcOID, &lineNo, command + 2 ))
    	{
    		Breakpoint breakpoint;
    
    		breakpoint.key.databaseId = MyProc->databaseId;
    		breakpoint.key.functionId = funcOID;
    		breakpoint.key.lineNumber = lineNo;
    		breakpoint.key.targetPid  = MyProc->pid;
    
    		if( BreakpointDelete( BP_LOCAL, &breakpoint.key ))
    
    		dbg_send( "f" );
    
    bool breakAtThisLine( Breakpoint ** dst, eBreakpointScope * scope, Oid funcOid, int lineNumber )
    
    {
    	BreakpointKey		key;
    
    	key.databaseId = MyProc->databaseId;
    	key.functionId = funcOid;
    
    	key.lineNumber = lineNumber;
    
    
    	if( per_session_ctx.step_into_next_func )
    	{
    		*dst   = NULL;
    		*scope = BP_LOCAL;
    		return( TRUE );
    	}
    
    
    Heikki Linnakangas's avatar
    Heikki Linnakangas committed
    	/*
    
    	 *  We conduct 3 searches here.
    	 *
    
    Heikki Linnakangas's avatar
    Heikki Linnakangas committed
    	 *	First, we look for a global breakpoint at this line, targeting our
    	 *  specific backend process.
    	 *
    	 *  Next, we look for a global breakpoint (at this line) that does
    	 *  not target a specific backend process.
    	 *
    
    	 *	Finally, we look for a local breakpoint at this line (implicitly
    
    Heikki Linnakangas's avatar
    Heikki Linnakangas committed
    	 *  targeting our specific backend process).
    	 *
    	 *	NOTE:  We must do the local-breakpoint search last because, when the
    	 *		   proxy attaches to our process, it marks all of its global
    	 *		   breakpoints as busy (so other potential targets will ignore
    	 *		   those breakpoints) and we copy all of those global breakpoints
    	 *		   into our local breakpoint hash.  If the debugger client exits
    	 *		   and the user starts another debugger session, we want to see the
    	 *		   new breakpoints instead of our obsolete local breakpoints (we
    	 *		   don't have a good way to detect that the proxy has disconnected
    	 *		   until it's inconvenient - we have to read-from or write-to the
    	 *		   proxy before we can tell that it's died).
    	 */
    
    
    	key.targetPid = MyProc->pid;		/* Search for a global breakpoint targeted at our process ID */
    
    	if((( *dst = BreakpointLookup( BP_GLOBAL, &key )) != NULL ) && ((*dst)->data.busy == FALSE ))
    	{
    		*scope = BP_GLOBAL;
    		return( TRUE );
    	}
    
    	key.targetPid = -1;					/* Search for a global breakpoint targeted at any process ID */
    
    	if((( *dst = BreakpointLookup( BP_GLOBAL, &key )) != NULL ) && ((*dst)->data.busy == FALSE ))
    	{
    		*scope = BP_GLOBAL;
    		return( TRUE );
    	}
    
    	key.targetPid = MyProc->pid;	 	/* Search for a local breakpoint (targeted at our process ID) */
    
    	if(( *dst = BreakpointLookup( BP_LOCAL, &key )) != NULL )
    	{
    		*scope = BP_LOCAL;
    		return( TRUE );
    	}
    
    	return( FALSE );
    
    bool breakpointsForFunction( Oid funcOid )
    
    {
    	if( BreakpointOnId( BP_LOCAL, funcOid ) || BreakpointOnId( BP_GLOBAL, funcOid ))
    		return( TRUE );
    	else
    		return( FALSE );
    
    }
    
    /* ---------------------------------------------------------------------
     * handle_socket_error()
     *
    
    Heikki Linnakangas's avatar
    Heikki Linnakangas committed
     * when invoked after a socket operation it would check socket operation's
    
     * last error status and invoke siglongjmp incase the error is fatal.
    
    	bool	fatal_err = TRUE;
    
    
    		switch(err)
    
    			case WSAEINTR:
    			case WSAEBADF:
    			case WSAEACCES:
    			case WSAEFAULT:
    			case WSAEINVAL:
    			case WSAEMFILE:
    
    			/*
    			 * Windows Sockets definitions of regular Berkeley error constants
    			 */
    			case WSAEWOULDBLOCK:
    			case WSAEINPROGRESS:
    			case WSAEALREADY:
    			case WSAENOTSOCK:
    			case WSAEDESTADDRREQ:
    			case WSAEMSGSIZE:
    			case WSAEPROTOTYPE:
    			case WSAENOPROTOOPT:
    			case WSAEPROTONOSUPPORT:
    			case WSAESOCKTNOSUPPORT:
    			case WSAEOPNOTSUPP:
    			case WSAEPFNOSUPPORT:
    			case WSAEAFNOSUPPORT:
    			case WSAEADDRINUSE:
    			case WSAEADDRNOTAVAIL:
    			case WSAENOBUFS:
    			case WSAEISCONN:
    			case WSAENOTCONN:
    			case WSAETOOMANYREFS:
    			case WSAETIMEDOUT:
    			case WSAELOOP:
    			case WSAENAMETOOLONG:
    			case WSAEHOSTUNREACH:
    			case WSAENOTEMPTY:
    			case WSAEPROCLIM:
    			case WSAEUSERS:
    			case WSAEDQUOT:
    			case WSAESTALE:
    			case WSAEREMOTE:
    
    			/*
    			 *	Extended Windows Sockets error constant definitions
    			 */
    			case WSASYSNOTREADY:
    			case WSAVERNOTSUPPORTED:
    			case WSANOTINITIALISED:
    			case WSAEDISCON:
    			case WSAENOMORE:
    			case WSAECANCELLED:
    			case WSAEINVALIDPROCTABLE:
    			case WSAEINVALIDPROVIDER:
    			case WSAEPROVIDERFAILEDINIT:
    			case WSASYSCALLFAILURE:
    			case WSASERVICE_NOT_FOUND:
    			case WSATYPE_NOT_FOUND:
    			case WSA_E_NO_MORE:
    			case WSA_E_CANCELLED:
    			case WSAEREFUSED:
    				break;
    
    			/*
    			 *	Server should shut down its socket on these errors.
    
    			case WSAENETDOWN:
    			case WSAENETUNREACH:
    			case WSAENETRESET:
    			case WSAECONNABORTED:
    			case WSAESHUTDOWN:
    			case WSAEHOSTDOWN:
    			case WSAECONNREFUSED:
    
    			case WSAECONNRESET:
    
    				fatal_err = TRUE;
    
    		if(fatal_err)
    
    		{
    			LPVOID lpMsgBuf;
    			FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,	NULL,err,
    					   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf,0, NULL );
    
    
    			elog(COMMERROR,"%s", (char *)lpMsgBuf);
    
    		switch(err)
    
    		{
    			case EINTR:
    			case ECONNREFUSED:
    			case EPIPE:
    			case ENOTCONN:
    
    				fatal_err = TRUE;
    
    			case ENOTSOCK:
    			case EAGAIN:
    			case EFAULT:
    			case ENOMEM:
    			case EINVAL:
    
    			default:
    
    		if(fatal_err)
    
    				elog(COMMERROR, "%s", strerror(err));
    
    			siglongjmp(client_lost.m_savepoint, 1);
    		}
    
    	return fatal_err;
    
    /*
     * Returns true if we continue stepping in this frame. False otherwise.
     */
    bool
    plugin_debugger_main_loop(void)
    {
    	ErrorContextCallback *frame;
    	debugger_language_t *lang; /* language of the selected frame */
    	bool	need_more     = TRUE;
    	char   *command;
    	bool	retval = TRUE;
    
    	/* Initially, set focus on the topmost frame in the stack */
    
    	for( frame = error_context_stack; frame; frame = frame->previous )
    	{
    		/*
    		 * ignore unrecognized stack frames.
    		 */
    		lang = language_of_frame(frame);
    		if (lang)
    			break;
    	}
    	if (frame == NULL)
    	{
    		/*
    		 * Oops, couldn't find a frame that we recognize in the stack. This
    		 * shouldn't happen since we're stopped at a breakpoint.
    		 */
    		elog(WARNING, "could not find PL/pgSQL frame at the top of the stack");
    		return false;
    	}
    
    	/* Report the current location */
    	lang->send_cur_line(frame);
    
    
    	 * Loop through the following chunk of code until we get a command
    	 * from the user that would let us execute this PL/pgSQL statement.
    	 */
    	while( need_more )
    	{
    		/* Wait for a command from the debugger client */