mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 14:15:49 -06:00
974 lines
26 KiB
C++
974 lines
26 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// pgAdmin III - PostgreSQL Tools
|
|
//
|
|
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
|
// This software is released under the PostgreSQL Licence
|
|
//
|
|
// dbgEvents.cpp - debugger controller events
|
|
// - This files contains the functions related to the event handling of the
|
|
// debugger controller.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "pgAdmin3.h"
|
|
|
|
// wxWindows headers
|
|
#include <wx/wx.h>
|
|
#include <wx/tokenzr.h>
|
|
|
|
#include "ctl/ctlAuiNotebook.h"
|
|
#include "db/pgConn.h"
|
|
#include "db/pgQueryThread.h"
|
|
#include "db/pgQueryResultEvent.h"
|
|
#include "schema/pgObject.h"
|
|
#include "debugger/dbgConst.h"
|
|
#include "debugger/dbgBreakPoint.h"
|
|
#include "debugger/dbgController.h"
|
|
#include "debugger/dbgModel.h"
|
|
#include "debugger/ctlStackWindow.h"
|
|
#include "debugger/ctlMessageWindow.h"
|
|
#include "debugger/ctlTabWindow.h"
|
|
#include "debugger/frmDebugger.h"
|
|
#include "debugger/dlgDirectDbg.h"
|
|
#include "utils/pgDefs.h"
|
|
|
|
#define LOG_DBG_MUTEX_LOCKING 0
|
|
|
|
#if LOG_DBG_MUTEX_LOCKING
|
|
|
|
#define LOCKMUTEX(m) \
|
|
wxPrintf(wxT("Mutex locking @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__); \
|
|
m.Lock(); \
|
|
wxPrintf(wxT("Mutex locked @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__);
|
|
|
|
#define UNLOCKMUTEX(m) \
|
|
wxPrintf(wxT("Mutex unlocking @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__); \
|
|
m.Unlock(); \
|
|
wxPrintf(wxT("Mutex unlocked @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__);
|
|
|
|
#else
|
|
|
|
#define LOCKMUTEX(m) \
|
|
m.Lock();
|
|
|
|
#define UNLOCKMUTEX(m) \
|
|
m.Unlock();
|
|
|
|
#endif
|
|
|
|
BEGIN_EVENT_TABLE(dbgController, wxEvtHandler)
|
|
EVT_BUTTON(MENU_ID_NOTICE_RECEIVED, dbgController::OnNoticeReceived)
|
|
EVT_BUTTON(MENU_ID_START_DEBUGGING, dbgController::OnStartDebugging)
|
|
|
|
EVT_PGQUERYRESULT(RESULT_ID_DIRECT_TARGET_COMPLETE, dbgController::ResultTargetComplete)
|
|
EVT_PGQUERYRESULT(RESULT_ID_ATTACH_TO_PORT, dbgController::ResultPortAttach)
|
|
EVT_PGQUERYRESULT(RESULT_ID_LISTENER_CREATED, dbgController::ResultListenerCreated)
|
|
|
|
EVT_PGQUERYRESULT(RESULT_ID_TARGET_READY, dbgController::ResultTargetReady)
|
|
|
|
EVT_PGQUERYRESULT(RESULT_ID_DEL_BREAKPOINT, dbgController::ResultDeletedBreakpoint)
|
|
|
|
EVT_PGQUERYRESULT(RESULT_ID_BREAKPOINT, dbgController::ResultBreakpoint)
|
|
EVT_PGQUERYRESULT(RESULT_ID_NEW_BREAKPOINT, dbgController::ResultNewBreakpoint)
|
|
EVT_PGQUERYRESULT(RESULT_ID_NEW_BREAKPOINT_WAIT, dbgController::ResultNewBreakpointWait)
|
|
|
|
EVT_PGQUERYRESULT(RESULT_ID_GET_VARS, dbgController::ResultVarList)
|
|
EVT_PGQUERYRESULT(RESULT_ID_GET_STACK, dbgController::ResultStack)
|
|
EVT_PGQUERYRESULT(RESULT_ID_GET_BREAKPOINTS, dbgController::ResultBreakpoints)
|
|
|
|
EVT_PGQUERYRESULT(RESULT_ID_DEPOSIT_VALUE, dbgController::ResultDepositValue)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
// Event functions
|
|
void dbgController::OnNoticeReceived(wxCommandEvent &_ev)
|
|
{
|
|
m_frm->GetMessageWindow()->AddMessage(_ev.GetString());
|
|
m_frm->GetTabWindow()->SelectTab(ID_MSG_PAGE);
|
|
}
|
|
|
|
|
|
void dbgController::OnStartDebugging(wxCommandEvent &_ev)
|
|
{
|
|
LOCKMUTEX(m_dbgThreadLock);
|
|
// Do not bother to process the result, if the debugger thread is not
|
|
// running or not exists
|
|
if (m_dbgThread && m_dbgThread->IsRunning())
|
|
{
|
|
m_dbgThread->AddQuery(
|
|
wxString::Format(ms_cmdAttachToPort, m_model->GetPort().c_str()),
|
|
NULL, RESULT_ID_ATTACH_TO_PORT);
|
|
}
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
}
|
|
|
|
|
|
void dbgController::ResultTargetComplete(pgQueryResultEvent &_ev)
|
|
{
|
|
pgBatchQuery *qry = _ev.GetQuery();
|
|
|
|
m_frm->EnableToolsAndMenus(false);
|
|
m_terminated = true;
|
|
m_frm->SetStatusText(_("Debugging Completed"));
|
|
m_frm->CloseProgressBar();
|
|
|
|
wxTheApp->Yield(true);
|
|
|
|
if (m_execConnThread)
|
|
{
|
|
switch(qry->ReturnCode())
|
|
{
|
|
case pgQueryResultEvent::PGQ_CONN_LOST:
|
|
wxLogInfo(
|
|
wxString::Format(
|
|
_("Debugger(%ld): Execution of the debugging target terminated due to connection loss.\n"),
|
|
m_execConnThread->GetId()));
|
|
break;
|
|
// This is very unlikely, unless we made mistake creating the query
|
|
case pgQueryResultEvent::PGQ_STRING_INVALID:
|
|
wxLogInfo(
|
|
wxString::Format(
|
|
_("Debugger(%ld): Execution of the debugging target terminated due to empty query string.\n"),
|
|
m_execConnThread->GetId()));
|
|
break;
|
|
case pgQueryResultEvent::PGQ_ERROR_PREPARE_CALLABLE:
|
|
case pgQueryResultEvent::PGQ_ERROR_EXECUTE_CALLABLE:
|
|
case pgQueryResultEvent::PGQ_ERROR_SEND_QUERY:
|
|
case pgQueryResultEvent::PGQ_ERROR_CONSUME_INPUT:
|
|
wxLogInfo(
|
|
wxString::Format(
|
|
_("Debugger(%ld): Execution of the debugging target terminated due to execution error (%d).\n"),
|
|
m_execConnThread->GetId(), qry->ReturnCode()));
|
|
break;
|
|
case pgQueryResultEvent::PGQ_EXECUTION_CANCELLED:
|
|
m_frm->SetStatusText(_("Debugging Cancelled"));
|
|
wxLogInfo(
|
|
wxString::Format(
|
|
_("Debugger(%ld): Execution of the debugging target cancelled.\n"),
|
|
m_execConnThread->GetId()));
|
|
break;
|
|
case pgQueryResultEvent::PGQ_RESULT_ERROR:
|
|
{
|
|
wxString strErr = qry->GetErrorMessage();
|
|
|
|
m_frm->SetStatusText(_("Execution completed with an error."));
|
|
wxLogInfo(
|
|
wxString::Format(
|
|
_("Debugger(%ld): Function/Procedure terminated with an error.\n%s"),
|
|
m_execConnThread->GetId(), strErr.c_str()));
|
|
|
|
m_frm->GetMessageWindow()->AddMessage(qry->GetMessage());
|
|
m_frm->GetMessageWindow()->SetFocus();
|
|
}
|
|
|
|
break;
|
|
case PGRES_FATAL_ERROR:
|
|
case PGRES_NONFATAL_ERROR:
|
|
case PGRES_BAD_RESPONSE:
|
|
{
|
|
wxString strErr = qry->GetErrorMessage();
|
|
m_frm->SetStatusText(_("Execution completed with an error."));
|
|
wxLogInfo(
|
|
wxString::Format(
|
|
_("Debugger(%ld): Function/Procedure terminated with an error.\n%s"),
|
|
m_execConnThread->GetId(), strErr.c_str()));
|
|
|
|
m_frm->GetMessageWindow()->AddMessage(qry->GetMessage());
|
|
m_frm->GetMessageWindow()->SetFocus();
|
|
}
|
|
|
|
break;
|
|
case PGRES_TUPLES_OK:
|
|
{
|
|
m_frm->SetStatusText(_("Execution completed."));
|
|
pgSet *set = qry->ResultSet();
|
|
m_frm->GetMessageWindow()->AddMessage(set->GetCommandStatus());
|
|
|
|
m_frm->GetResultWindow()->FillResult(set);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
m_frm->SetStatusText(_("Execution completed."));
|
|
wxLogInfo(
|
|
wxString::Format(
|
|
_("Debugger(%ld): Execution of the debugging function/procedure completed\n"),
|
|
m_execConnThread->GetId()));
|
|
|
|
m_frm->GetMessageWindow()->AddMessage(qry->GetMessage());
|
|
m_frm->GetMessageWindow()->SetFocus();
|
|
|
|
break;
|
|
}
|
|
|
|
if (m_execConnThread->IsRunning())
|
|
{
|
|
m_execConnThread->CancelExecution();
|
|
m_execConnThread->Wait();
|
|
}
|
|
|
|
pgConn *execConn = m_execConnThread->GetConn();
|
|
delete m_execConnThread;
|
|
|
|
m_execConnThread = NULL;
|
|
execConn->Close();
|
|
delete execConn;
|
|
}
|
|
|
|
LOCKMUTEX(m_dbgThreadLock);
|
|
pgQueryThread *oldDebugThread = m_dbgThread;
|
|
m_dbgThread = NULL;
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
|
|
if (oldDebugThread)
|
|
{
|
|
oldDebugThread->CancelExecution();
|
|
oldDebugThread->Wait();
|
|
|
|
delete oldDebugThread;
|
|
}
|
|
|
|
// We won't keep the connection open, we will open it only when required
|
|
if (m_dbgConn)
|
|
m_dbgConn->Close();
|
|
|
|
if (m_frm)
|
|
m_frm->EnableToolsAndMenus(false);
|
|
}
|
|
|
|
|
|
void dbgController::ResultPortAttach(pgQueryResultEvent &_ev)
|
|
{
|
|
pgBatchQuery *qry = _ev.GetQuery();
|
|
|
|
if (!HandleQuery(qry, _("Error attaching the proxy session.")))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (qry->ReturnCode() != PGRES_TUPLES_OK)
|
|
{
|
|
Stop();
|
|
|
|
return;
|
|
}
|
|
|
|
m_frm->EnableToolsAndMenus(false);
|
|
|
|
wxString strSession = qry->ResultSet()->GetVal(0);
|
|
m_model->GetSession() = strSession;
|
|
|
|
pgParamsArray *params = new pgParamsArray;
|
|
|
|
params->Add(new pgParam(PGOID_TYPE_INT4, &strSession));
|
|
|
|
LOCKMUTEX(m_dbgThreadLock);
|
|
if (m_dbgThread && m_dbgThread->IsRunning())
|
|
{
|
|
if (m_ver <= DEBUGGER_V2_API)
|
|
{
|
|
m_dbgThread->AddQuery(ms_cmdWaitForBreakpointV1, params, RESULT_ID_BREAKPOINT);
|
|
}
|
|
else
|
|
{
|
|
m_dbgThread->AddQuery(ms_cmdWaitForBreakpointV2, params, RESULT_ID_BREAKPOINT);
|
|
}
|
|
qry->Release();
|
|
|
|
// Now create any breakpoints that the user created in last execution. We
|
|
// start by asking the server to resolve the breakpoint name into an OID (or,
|
|
// a pair of OID's if the target is defined in a package). As each
|
|
// (targetInof) result arrives, we add a break-point at the resulting OID.
|
|
dbgBreakPointList breakpoints = m_model->GetBreakPoints();
|
|
|
|
for (dbgBreakPointList::Node *node = breakpoints.GetFirst(); node;
|
|
node = node->GetNext())
|
|
{
|
|
dbgBreakPoint *breakpoint = node->GetData();
|
|
/*
|
|
* In EnterpriseDB versions <= 9.1 the
|
|
* pldbg_set_global_breakpoint function took five arguments,
|
|
* the 2nd argument being the package's OID, if any. Starting
|
|
* with 9.2, the package OID argument is gone, and the function
|
|
* takes four arguments like the community version has always
|
|
* done.
|
|
*/
|
|
if (m_dbgConn->GetIsEdb() && !m_dbgConn->BackendMinimumVersion(9, 2))
|
|
{
|
|
m_dbgThread->AddQuery(
|
|
wxString::Format(
|
|
ms_cmdSetBreakpointV1, m_model->GetSession().c_str(),
|
|
breakpoint->GetPackageOid().c_str(),
|
|
breakpoint->GetFunctionOid().c_str(),
|
|
breakpoint->GetLineNo()),
|
|
NULL, RESULT_ID_NEW_BREAKPOINT);
|
|
}
|
|
else
|
|
{
|
|
m_dbgThread->AddQuery(
|
|
wxString::Format(
|
|
ms_cmdSetBreakpointV2, m_model->GetSession().c_str(),
|
|
breakpoint->GetFunctionOid().c_str(),
|
|
breakpoint->GetLineNo()),
|
|
NULL, RESULT_ID_NEW_BREAKPOINT);
|
|
}
|
|
}
|
|
}
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
}
|
|
|
|
|
|
bool dbgController::HandleQuery(pgBatchQuery *_qry, const wxString &_err)
|
|
{
|
|
if (!_qry)
|
|
return false;
|
|
|
|
LOCKMUTEX(m_dbgThreadLock);
|
|
|
|
// It is possible that we found one error, while running the previous query
|
|
// and, called Stop function from it, because of an error found.
|
|
// That may have released the debugger thread
|
|
//
|
|
// In this case, we should exit this flow gracefully instead showing any
|
|
// error
|
|
if (!m_dbgThread || !m_dbgThread->IsRunning())
|
|
{
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
return false;
|
|
}
|
|
|
|
switch(_qry->ReturnCode())
|
|
{
|
|
case pgQueryResultEvent::PGQ_CONN_LOST:
|
|
// This is very unlikely, unless we made mistake creating the query
|
|
case pgQueryResultEvent::PGQ_STRING_INVALID:
|
|
case PGRES_EMPTY_QUERY:
|
|
case pgQueryResultEvent::PGQ_ERROR_PREPARE_CALLABLE:
|
|
case pgQueryResultEvent::PGQ_ERROR_EXECUTE_CALLABLE:
|
|
case pgQueryResultEvent::PGQ_ERROR_SEND_QUERY:
|
|
case pgQueryResultEvent::PGQ_ERROR_CONSUME_INPUT:
|
|
case pgQueryResultEvent::PGQ_RESULT_ERROR:
|
|
case PGRES_BAD_RESPONSE:
|
|
case PGRES_NONFATAL_ERROR:
|
|
case PGRES_FATAL_ERROR:
|
|
break;
|
|
|
|
// This is unlikely, we do not generate an event, when execution is
|
|
// cancelled
|
|
case pgQueryResultEvent::PGQ_EXECUTION_CANCELLED:
|
|
break;
|
|
case PGRES_COMMAND_OK:
|
|
case PGRES_TUPLES_OK:
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
return true;
|
|
// Hmm - where did it come from?
|
|
// We never call a function, which results into these results
|
|
// Anyway - we will return true as we have the result
|
|
case PGRES_COPY_IN:
|
|
case PGRES_COPY_OUT:
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
return true;
|
|
}
|
|
|
|
m_frm->EnableToolsAndMenus(false);
|
|
wxTheApp->Yield(true);
|
|
|
|
if (!m_dbgConn->IsAlive())
|
|
{
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
wxMessageBox(
|
|
_("Connection to the database server lost, debugging cannot continue!"),
|
|
_("Connection Lost"), wxICON_ERROR | wxOK);
|
|
}
|
|
else
|
|
{
|
|
wxString strErr;
|
|
if (!_err.IsEmpty())
|
|
strErr = _err + wxT("\n\n");
|
|
strErr += _qry->GetErrorMessage();
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
|
|
if (_qry->ReturnCode() == PGRES_FATAL_ERROR)
|
|
{
|
|
// We will start start listening for new in-context session, if the
|
|
// current session is closed. On which, the query/target was
|
|
// running.
|
|
//
|
|
// This allows us to have the same behaviour as the old one.
|
|
//
|
|
if (m_sessionType == DBG_SESSION_TYPE_INCONTEXT && m_currTargetPid != wxT(""))
|
|
{
|
|
// Let's check if the target pid has stopped or exited after
|
|
// successful debugging, let's move on and wait for the next
|
|
// target to hit.
|
|
wxString isTargetRunning = m_dbgConn->ExecuteScalar(wxString::Format(ms_cmdIsBackendRunning, m_currTargetPid.c_str()));
|
|
|
|
if (isTargetRunning == wxT("0"))
|
|
{
|
|
// Reset the current backend-pid of the target
|
|
m_currTargetPid = wxT("");
|
|
m_dbgThread->AddQuery(
|
|
wxString::Format(
|
|
ms_cmdWaitForTarget, m_model->GetSession().c_str()),
|
|
NULL, RESULT_ID_TARGET_READY);
|
|
|
|
m_frm->LaunchWaitingDialog();
|
|
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wxMessageBox(_("The calling connection was closed or lost."), _("Connection Lost"), wxICON_ERROR | wxOK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wxMessageBox(strErr, _("Execution Error"), wxICON_ERROR | wxOK);
|
|
}
|
|
|
|
wxLogQuietError(strErr);
|
|
}
|
|
|
|
Stop();
|
|
m_terminated = true;
|
|
|
|
m_frm->SetStatusText(_("Debugging aborting..."));
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void dbgController::ResultBreakpoint(pgQueryResultEvent &_ev)
|
|
{
|
|
if (m_frm)
|
|
m_frm->CloseProgressBar();
|
|
|
|
pgBatchQuery *qry = _ev.GetQuery();
|
|
|
|
if (!HandleQuery(qry, wxEmptyString))
|
|
return;
|
|
|
|
LOCKMUTEX(m_dbgThreadLock);
|
|
// Do not bother to process the result, if the debugger thread is not
|
|
// running or not exists
|
|
if (m_dbgThread && m_dbgThread->IsRunning())
|
|
{
|
|
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
|
{
|
|
wxString func,
|
|
pkg = wxT("0");
|
|
|
|
pgSet *set = qry->ResultSet();
|
|
|
|
if (set->HasColumn(wxT("pkg")))
|
|
{
|
|
pkg = set->GetVal(wxT("pkg"));
|
|
}
|
|
func = set->GetVal(wxT("func"));
|
|
|
|
m_model->GetFocusedPackage() = pkg;
|
|
m_model->GetFocusedFunction() = func;
|
|
|
|
int lineNo = (int)(set->GetLong(wxT("linenumber")) - 1);
|
|
|
|
if (lineNo < 0)
|
|
lineNo = -1;
|
|
else
|
|
lineNo -= m_lineOffset;
|
|
m_model->GetCurrLineNo() = lineNo;
|
|
|
|
wxString strSource = set->GetVal(wxT("src"));
|
|
|
|
if (strSource.IsEmpty())
|
|
strSource = _("<source not available>");
|
|
|
|
dbgCachedStack src(pkg, func, set->GetVal(wxT("targetname")),
|
|
set->GetVal(wxT("args")), strSource);
|
|
|
|
m_model->AddSource(func, src);
|
|
m_frm->DisplaySource(src);
|
|
|
|
m_dbgThread->AddQuery(
|
|
wxString::Format(ms_cmdGetStack, m_model->GetSession().c_str()),
|
|
NULL, RESULT_ID_GET_STACK);
|
|
|
|
m_frm->EnableToolsAndMenus(true);
|
|
}
|
|
|
|
// Release the result-set
|
|
qry->Release();
|
|
}
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
}
|
|
|
|
|
|
void dbgController::ResultVarList(pgQueryResultEvent &_ev)
|
|
{
|
|
pgBatchQuery *qry = _ev.GetQuery();
|
|
|
|
if (!HandleQuery(qry, _("Error fetching variables.")))
|
|
return;
|
|
|
|
LOCKMUTEX(m_dbgThreadLock);
|
|
// Do not bother to process the result, if the debugger thread is not
|
|
// running or not exists
|
|
if (m_dbgThread && m_dbgThread->IsRunning())
|
|
{
|
|
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
|
{
|
|
ctlVarWindow *paramWin = NULL, *pkgVarWin = NULL, *varWin = NULL;
|
|
pgSet *set = qry->ResultSet();
|
|
|
|
while (!set->Eof())
|
|
{
|
|
switch((char)(set->GetVal(wxT("varclass"))[0]))
|
|
{
|
|
case 'A':
|
|
{
|
|
if (paramWin == NULL)
|
|
paramWin = m_frm->GetParamWindow(true);
|
|
|
|
paramWin->AddVar(
|
|
set->GetVal(wxT("name")),
|
|
set->GetVal(wxT("value")),
|
|
set->GetVal(wxT("dtype")),
|
|
set->GetBool(wxT("isconst")));
|
|
|
|
break;
|
|
}
|
|
case 'P':
|
|
{
|
|
if (pkgVarWin == NULL)
|
|
pkgVarWin = m_frm->GetPkgVarWindow(true);
|
|
|
|
pkgVarWin->AddVar(
|
|
set->GetVal(wxT("name")),
|
|
set->GetVal(wxT("value")),
|
|
set->GetVal(wxT("dtype")),
|
|
set->GetBool(wxT("isconst")));
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
if (varWin == NULL)
|
|
varWin = m_frm->GetVarWindow(true);
|
|
|
|
varWin->AddVar(
|
|
set->GetVal(wxT("name")),
|
|
set->GetVal(wxT("value")),
|
|
set->GetVal(wxT("dtype")),
|
|
set->GetBool(wxT("isconst")));
|
|
|
|
break;
|
|
}
|
|
}
|
|
set->MoveNext();
|
|
}
|
|
}
|
|
|
|
// Release the result-set
|
|
qry->Release();
|
|
}
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
}
|
|
|
|
|
|
void dbgController::ResultStack(pgQueryResultEvent &_ev)
|
|
{
|
|
pgBatchQuery *qry = _ev.GetQuery();
|
|
|
|
if (!HandleQuery(qry, _("Error fetching the call stack.")))
|
|
return;
|
|
|
|
LOCKMUTEX(m_dbgThreadLock);
|
|
// Do not bother to process the result, if the debugger thread is not
|
|
// running or not exists
|
|
if (m_dbgThread && m_dbgThread->IsRunning())
|
|
{
|
|
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
|
{
|
|
pgSet *set = qry->ResultSet();
|
|
|
|
dbgStackFrameList stacks;
|
|
ctlStackWindow *stackWin = m_frm->GetStackWindow();
|
|
|
|
// Fetched the stack, now fetch the break-points
|
|
m_dbgThread->AddQuery(
|
|
wxString::Format(ms_cmdGetBreakpoints, m_model->GetSession().c_str()),
|
|
NULL, RESULT_ID_GET_BREAKPOINTS);
|
|
|
|
stackWin->ClearStack();
|
|
|
|
int selected = 0,
|
|
levelCol = set->ColNumber(wxT("level")),
|
|
pkgCol = set->HasColumn(wxT("pkg")) ? set->ColNumber(wxT("level")) : -1,
|
|
funCol = set->ColNumber(wxT("func")),
|
|
targetCol = set->ColNumber(wxT("targetname")),
|
|
argsCol = set->ColNumber(wxT("args")),
|
|
lineCol = set->ColNumber(wxT("linenumber"));
|
|
|
|
while (!set->Eof())
|
|
{
|
|
// The result set contains one tuple per frame:
|
|
// package, function, linenumber, args
|
|
dbgStackFrame *frame = new dbgStackFrame(
|
|
set->GetVal(levelCol),
|
|
pkgCol != -1 ? set->GetVal(wxT("pkg")) : wxT("0"),
|
|
set->GetVal(funCol),
|
|
wxString::Format(
|
|
wxT("%s(%s)@%s"),
|
|
set->GetVal(targetCol).c_str(),
|
|
set->GetVal(argsCol).c_str(),
|
|
set->GetVal(lineCol).c_str()));
|
|
|
|
// Select this one in the stack window
|
|
if (frame->GetFunction() == m_model->GetDisplayedFunction() &&
|
|
frame->GetPackage() == m_model->GetDisplayedPackage())
|
|
{
|
|
selected = set->CurrentPos() - 1;
|
|
}
|
|
|
|
stacks.Append(frame);
|
|
set->MoveNext();
|
|
}
|
|
stackWin->SetStack(stacks, selected);
|
|
}
|
|
|
|
// Release the result-set
|
|
qry->Release();
|
|
}
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
}
|
|
|
|
|
|
void dbgController::ResultBreakpoints(pgQueryResultEvent &_ev)
|
|
{
|
|
pgBatchQuery *qry = _ev.GetQuery();
|
|
|
|
if (!HandleQuery(qry, _("Error fetching breakpoints.")))
|
|
return;
|
|
|
|
LOCKMUTEX(m_dbgThreadLock);
|
|
// Do not bother to process the result, if the debugger thread is not
|
|
// running or not exists
|
|
if (m_dbgThread && m_dbgThread->IsRunning())
|
|
{
|
|
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
|
{
|
|
pgSet *set = qry->ResultSet();
|
|
|
|
int pkgCol = -1, funcCol = set->ColNumber(wxT("func")),
|
|
lineCol = set->ColNumber(wxT("linenumber"));
|
|
|
|
if (set->HasColumn(wxT("pkg")))
|
|
{
|
|
pkgCol = set->ColNumber(wxT("pkg"));
|
|
}
|
|
|
|
m_dbgThread->AddQuery(
|
|
wxString::Format(ms_cmdGetVars, m_model->GetSession().c_str()),
|
|
NULL, RESULT_ID_GET_VARS);
|
|
|
|
m_frm->ClearBreakpointMarkers();
|
|
dbgBreakPointList &breakpoints = m_model->GetBreakPoints();
|
|
WX_CLEAR_ARRAY(breakpoints);
|
|
|
|
while (!set->Eof())
|
|
{
|
|
// The result set contains one tuple per breakpoint:
|
|
// pkg, func, linenumber, target
|
|
// or,
|
|
// func, linenumber, target
|
|
wxString pkg = (pkgCol == -1) ? wxT("0") : set->GetVal(pkgCol);
|
|
wxString func = set->GetVal(funcCol);
|
|
int lineNumber = (int)set->GetLong(lineCol);
|
|
|
|
// Save this break-points in break-point list
|
|
breakpoints.Append(new dbgBreakPoint(func, pkg, lineNumber));
|
|
|
|
// Mark the break-point in the viewer
|
|
if (pkg == m_model->GetDisplayedPackage() &&
|
|
func == m_model->GetDisplayedFunction())
|
|
{
|
|
m_frm->MarkBreakpoint(lineNumber - 1);
|
|
}
|
|
set->MoveNext();
|
|
}
|
|
}
|
|
|
|
// Release the result-set
|
|
qry->Release();
|
|
}
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
}
|
|
|
|
|
|
void dbgController::ResultNewBreakpoint(pgQueryResultEvent &_ev)
|
|
{
|
|
// We will wait for the last new break-point, which will be handled in
|
|
// dbgController::ResultNewBreakpointWait function
|
|
if (!HandleQuery(_ev.GetQuery(), _("Could not create the breakpoint.")))
|
|
return;
|
|
|
|
LOCKMUTEX(m_dbgThreadLock);
|
|
// Do not bother to process the result, if the debugger thread is not
|
|
// running or not exists
|
|
if (m_dbgThread && m_dbgThread->IsRunning())
|
|
{
|
|
// Release the result-set
|
|
_ev.GetQuery()->Release();
|
|
}
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
}
|
|
|
|
|
|
void dbgController::ResultNewBreakpointWait(pgQueryResultEvent &_ev)
|
|
{
|
|
pgBatchQuery *qry = _ev.GetQuery();
|
|
|
|
if (!HandleQuery(qry, _("Could not create the breakpoint.")))
|
|
return;
|
|
|
|
LOCKMUTEX(m_dbgThreadLock);
|
|
// Do not bother to process the result, if the debugger thread is not
|
|
// running or not exists
|
|
if (m_dbgThread && m_dbgThread->IsRunning())
|
|
{
|
|
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
|
{
|
|
pgSet *set = _ev.GetQuery()->ResultSet();
|
|
m_frm->EnableToolsAndMenus(false);
|
|
|
|
m_dbgThread->AddQuery(
|
|
wxString::Format(
|
|
ms_cmdWaitForTarget, m_model->GetSession().c_str()),
|
|
NULL, RESULT_ID_TARGET_READY);
|
|
|
|
m_frm->LaunchWaitingDialog();
|
|
}
|
|
|
|
// Release the result-set
|
|
qry->Release();
|
|
}
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
}
|
|
|
|
|
|
void dbgController::ResultDeletedBreakpoint(pgQueryResultEvent &_ev)
|
|
{
|
|
pgBatchQuery *qry = _ev.GetQuery();
|
|
|
|
if (!HandleQuery(qry, _("Could not drop the breakpoint.")))
|
|
return;
|
|
|
|
LOCKMUTEX(m_dbgThreadLock);
|
|
// Do not bother to process the result, if the debugger thread is not
|
|
// running or not exists
|
|
if (m_dbgThread && m_dbgThread->IsRunning())
|
|
{
|
|
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
|
{
|
|
pgSet *set = qry->ResultSet();
|
|
|
|
if (set->GetBool(0))
|
|
{
|
|
m_frm->SetStatusText(_("Breakpoint dropped"));
|
|
}
|
|
}
|
|
|
|
// Release the result-set
|
|
qry->Release();
|
|
}
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// ResultDepositValue()
|
|
//
|
|
// This event handler is called when the proxy completes a 'deposit' operation
|
|
// (in response to an earlier call to pldbg_deposit_value()).
|
|
//
|
|
// We schedule a refresh of our variable window(s) for the next idle period.
|
|
//
|
|
void dbgController::ResultDepositValue(pgQueryResultEvent &_ev)
|
|
{
|
|
pgBatchQuery *qry = _ev.GetQuery();
|
|
|
|
if (!HandleQuery(qry, _("Could not deposit the new value.")))
|
|
return;
|
|
|
|
LOCKMUTEX(m_dbgThreadLock);
|
|
// Do not bother to process the result, if the debugger thread is not
|
|
// running or not exists
|
|
if (m_dbgThread && m_dbgThread->IsRunning())
|
|
{
|
|
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
|
{
|
|
pgSet *set = qry->ResultSet();
|
|
|
|
if (set->GetBool(0))
|
|
{
|
|
m_frm->SetStatusText(_("Value changed"));
|
|
|
|
m_dbgThread->AddQuery(
|
|
wxString::Format(ms_cmdGetVars, m_model->GetSession().c_str()),
|
|
NULL, RESULT_ID_GET_VARS);
|
|
}
|
|
else
|
|
{
|
|
wxLogError(_("Could not deposit the new value."));
|
|
}
|
|
}
|
|
|
|
// Release the result-set
|
|
qry->Release();
|
|
}
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
}
|
|
|
|
|
|
void dbgController::ResultListenerCreated(pgQueryResultEvent &_ev)
|
|
{
|
|
pgBatchQuery *qry = _ev.GetQuery();
|
|
|
|
if (!HandleQuery(qry,
|
|
_("Could not create the proxy listener for in-process debugging.")))
|
|
return;
|
|
|
|
LOCKMUTEX(m_dbgThreadLock);
|
|
// Do not bother to process the result, if the debugger thread is not
|
|
// running or not exists
|
|
if (m_dbgThread && m_dbgThread->IsRunning())
|
|
{
|
|
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
|
{
|
|
pgSet *set = qry->ResultSet();
|
|
|
|
// We now have a global listener and a session handle. Grab the session
|
|
// handle (we'll need it for just about everything else).
|
|
m_model->GetSession() = set->GetVal(0);
|
|
|
|
// Release the result-set
|
|
qry->Release();
|
|
|
|
// Now create any global breakpoints that the user requested. We start
|
|
// by asking the server to resolve the breakpoint name into an OID (or,
|
|
// a pair of OID's if the target is defined in a package). As each
|
|
// (targetInof) result arrives, we add a break-point at the resulting
|
|
// OID.
|
|
unsigned int idx = 1;
|
|
long resultId;
|
|
|
|
dbgBreakPointList breakpoints = m_model->GetBreakPoints();
|
|
for (dbgBreakPointList::Node *node = breakpoints.GetFirst(); node;
|
|
node = node->GetNext(), ++idx)
|
|
{
|
|
dbgBreakPoint *breakpoint = node->GetData();
|
|
|
|
if(idx < breakpoints.GetCount())
|
|
{
|
|
resultId = RESULT_ID_NEW_BREAKPOINT;
|
|
}
|
|
else
|
|
{
|
|
resultId = RESULT_ID_NEW_BREAKPOINT_WAIT;
|
|
}
|
|
|
|
/*
|
|
* In EnterpriseDB versions <= 9.1 the
|
|
* pldbg_set_global_breakpoint function took five arguments,
|
|
* the 2nd argument being the package's OID, if any. Starting
|
|
* with 9.2, the package OID argument is gone, and the function
|
|
* takes four arguments like the community version has always
|
|
* done.
|
|
*/
|
|
if (m_dbgConn->GetIsEdb() && !m_dbgConn->BackendMinimumVersion(9, 2))
|
|
{
|
|
m_dbgThread->AddQuery(
|
|
wxString::Format(
|
|
ms_cmdAddBreakpointEDB, m_model->GetSession().c_str(),
|
|
breakpoint->GetPackageOid().c_str(),
|
|
breakpoint->GetFunctionOid().c_str(),
|
|
m_model->GetTargetPid().c_str()),
|
|
NULL, resultId);
|
|
}
|
|
else
|
|
{
|
|
m_dbgThread->AddQuery(
|
|
wxString::Format(
|
|
ms_cmdAddBreakpointPG, m_model->GetSession().c_str(),
|
|
breakpoint->GetFunctionOid().c_str(),
|
|
m_model->GetTargetPid().c_str()),
|
|
NULL, resultId);
|
|
}
|
|
}
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
}
|
|
else
|
|
{
|
|
// Release the result-set
|
|
qry->Release();
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
}
|
|
}
|
|
|
|
|
|
void dbgController::ResultTargetReady(pgQueryResultEvent &_ev)
|
|
{
|
|
pgBatchQuery *qry = _ev.GetQuery();
|
|
|
|
if (m_frm)
|
|
{
|
|
m_frm->CloseProgressBar();
|
|
m_frm->EnableToolsAndMenus(false);
|
|
}
|
|
|
|
if (!HandleQuery(qry,
|
|
_("Error fetching target information.")))
|
|
return;
|
|
|
|
LOCKMUTEX(m_dbgThreadLock);
|
|
// Do not bother to process the result, if the debugger thread is not
|
|
// running or not exists
|
|
if (m_dbgThread && m_dbgThread->IsRunning())
|
|
{
|
|
bool goAhead = false;
|
|
goAhead = (qry->ReturnCode() == PGRES_TUPLES_OK);
|
|
pgSet *set = qry->ResultSet();
|
|
|
|
// Save the current running target pid
|
|
m_currTargetPid = set->GetVal(0);
|
|
|
|
// Next line release the actual result-set
|
|
set = NULL;
|
|
|
|
// Release the result-set
|
|
qry->Release();
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
|
|
pgParamsArray *params = new pgParamsArray;
|
|
|
|
params->Add(new pgParam(PGOID_TYPE_INT4, &(m_model->GetSession())));
|
|
if (m_ver <= DEBUGGER_V2_API)
|
|
{
|
|
m_dbgThread->AddQuery(ms_cmdWaitForBreakpointV1, params, RESULT_ID_BREAKPOINT);
|
|
}
|
|
else
|
|
{
|
|
m_dbgThread->AddQuery(ms_cmdWaitForBreakpointV2, params, RESULT_ID_BREAKPOINT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UNLOCKMUTEX(m_dbgThreadLock);
|
|
}
|
|
}
|