From 172a1ae4927035b6d3d57a0b2e309a05dec545de Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 3 Mar 2010 18:08:18 +0100 Subject: [PATCH] debugger: sanitize breakpoint setting sequences --- src/plugins/debugger/gdb/gdbengine.cpp | 100 +++++++++++-------- src/plugins/debugger/gdb/gdbengine.h | 40 +++++--- src/plugins/debugger/gdb/pythongdbengine.cpp | 2 + 3 files changed, 87 insertions(+), 55 deletions(-) diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 4b6ccc83e2c..6babfa4fc26 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -255,10 +255,10 @@ void GdbEngine::initializeVariables() invalidateSourcesList(); m_sourcesListUpdating = false; - m_breakListUpdating = false; m_oldestAcceptableToken = -1; m_outputCodec = QTextCodec::codecForLocale(); m_pendingWatchRequests = 0; + m_pendingBreakpointRequests = 0; m_commandsDoneCallback = 0; m_commandsToRunOnTemporaryBreak.clear(); m_cookieForToken.clear(); @@ -719,11 +719,16 @@ void GdbEngine::postCommandHelper(const GdbCommand &cmd) if (cmd.flags & RebuildWatchModel) { ++m_pendingWatchRequests; - PENDING_DEBUG(" MODEL:" << cmd.command << "=>" << cmd.callbackName + PENDING_DEBUG(" WATCH MODEL:" << cmd.command << "=>" << cmd.callbackName << "INCREMENTS PENDING TO" << m_pendingWatchRequests); + } else if (cmd.flags & RebuildBreakpointModel) { + ++m_pendingBreakpointRequests; + PENDING_DEBUG(" BRWAKPOINT MODEL:" << cmd.command << "=>" << cmd.callbackName + << "INCREMENTS PENDING TO" << m_pendingBreakpointRequests); } else { PENDING_DEBUG(" OTHER (IN):" << cmd.command << "=>" << cmd.callbackName - << "LEAVES PENDING AT" << m_pendingWatchRequests); + << "LEAVES PENDING WATCH AT" << m_pendingWatchRequests + << "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests); } if ((cmd.flags & NeedsStop) || !m_commandsToRunOnTemporaryBreak.isEmpty()) { @@ -942,14 +947,23 @@ void GdbEngine::handleResultRecord(GdbResponse *response) if (cmd.flags & RebuildWatchModel) { --m_pendingWatchRequests; PENDING_DEBUG(" WATCH" << cmd.command << "=>" << cmd.callbackName - << "DECREMENTS PENDING TO" << m_pendingWatchRequests); + << "DECREMENTS PENDING WATCH TO" << m_pendingWatchRequests); if (m_pendingWatchRequests <= 0) { - PENDING_DEBUG("\n\n ... AND TRIGGERS MODEL UPDATE\n"); + PENDING_DEBUG("\n\n ... AND TRIGGERS WATCH MODEL UPDATE\n"); rebuildWatchModel(); } + } else if (cmd.flags & RebuildBreakpointModel) { + --m_pendingBreakpointRequests; + PENDING_DEBUG(" BREAKPOINT" << cmd.command << "=>" << cmd.callbackName + << "DECREMENTS PENDING TO" << m_pendingWatchRequests); + if (m_pendingBreakpointRequests <= 0) { + PENDING_DEBUG("\n\n ... AND TRIGGERS BREAKPOINT MODEL UPDATE\n"); + attemptBreakpointSynchronization(); + } } else { PENDING_DEBUG(" OTHER (OUT):" << cmd.command << "=>" << cmd.callbackName - << "LEAVES PENDING AT" << m_pendingWatchRequests); + << "LEAVES PENDING WATCH AT" << m_pendingWatchRequests + << "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests); } // Commands were queued, but we were in RunningRequested state, so the interrupt @@ -1103,7 +1117,6 @@ void GdbEngine::handleAqcuiredInferior() // It's nicer to see a bit of the world we live in. reloadModulesInternal(); - attemptBreakpointSynchronization(); } #endif @@ -1312,20 +1325,22 @@ void GdbEngine::handleStop1(const GdbMi &data) return; } + // This is for display only. if (m_modulesListOutdated) - reloadModulesInternal(); // This is for display only - if (m_sourcesListOutdated && theDebuggerBoolSetting(UsePreciseBreakpoints)) - reloadSourceFilesInternal(); // This needs to be done before fullName() may need it + reloadModulesInternal(); - if (!hasPython()) { - if (m_breakListOutdated) + // This needs to be done before fullName() may need it. + if (m_sourcesListOutdated && theDebuggerBoolSetting(UsePreciseBreakpoints)) + reloadSourceFilesInternal(); + + if (m_breakListOutdated) { + reloadBreakListInternal(); + } else { + // Older gdb versions do not produce "library loaded" messages + // so the breakpoint update is not triggered. + if (m_gdbVersion < 70000 && !m_isMacGdb + && manager()->breakHandler()->size() > 0) reloadBreakListInternal(); - else - // Older gdb versions do not produce "library loaded" messages - // so the breakpoint update is not triggered. - if (m_gdbVersion < 70000 && !m_isMacGdb && !m_breakListUpdating - && manager()->breakHandler()->size() > 0) - reloadBreakListInternal(); } if (reason == "breakpoint-hit") { @@ -2035,8 +2050,7 @@ void GdbEngine::breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt QString GdbEngine::breakLocation(const QString &file) const { - QTC_ASSERT(!m_breakListOutdated, /* */) - QTC_ASSERT(!m_breakListUpdating, /* */) + //QTC_ASSERT(!m_breakListOutdated, /* */) QString where = m_fullToShortName.value(file); if (where.isEmpty()) return QFileInfo(file).fileName(); @@ -2057,28 +2071,30 @@ void GdbEngine::sendInsertBreakpoint(int index) where = data->funcName.toLatin1(); } - // set up fallback in case of pending breakpoints which aren't handled - // by the MI interface + // Set up fallback in case of pending breakpoints which aren't handled + // by the MI interface. QByteArray cmd; if (m_isMacGdb) cmd = "-break-insert -l -1 -f "; else if (m_gdbAdapter->isTrkAdapter()) cmd = "-break-insert -h -f "; - else if (m_gdbVersion >= 60800) // Probably some earlier version would work as well ... + else if (m_gdbVersion >= 60800) + // Probably some earlier version would work as well. cmd = "-break-insert -f "; else cmd = "-break-insert "; //if (!data->condition.isEmpty()) // cmd += "-c " + data->condition + ' '; cmd += where; - postCommand(cmd, NeedsStop, CB(handleBreakInsert), index); + postCommand(cmd, NeedsStop | RebuildBreakpointModel, + CB(handleBreakInsert), index); } void GdbEngine::reloadBreakListInternal() { - m_breakListUpdating = true; - // "Discardable" as long as we do in each step - postCommand("-break-list", NeedsStop | Discardable, CB(handleBreakList)); + postCommand("-break-list", + NeedsStop | RebuildBreakpointModel, + CB(handleBreakList)); } void GdbEngine::handleBreakList(const GdbResponse &response) @@ -2129,9 +2145,7 @@ void GdbEngine::handleBreakList(const GdbMi &table) //qDebug() << "CANNOT HANDLE RESPONSE" << bkpts.at(index).toString(); } - m_breakListUpdating = false; m_breakListOutdated = false; - attemptBreakpointSynchronization(); } void GdbEngine::handleBreakIgnore(const GdbResponse &response) @@ -2196,7 +2210,6 @@ void GdbEngine::handleBreakInsert(const GdbResponse &response) GdbMi bkpt = response.data.findChild("bkpt"); breakpointDataFromOutput(data, bkpt); //#endif - attemptBreakpointSynchronization(); } else { if (m_gdbVersion < 60800 && !m_isMacGdb) { // This gdb version doesn't "do" pending breakpoints. @@ -2267,7 +2280,6 @@ void GdbEngine::handleBreakInfo(const GdbResponse &response) QString str = QString::fromLocal8Bit( response.data.findChild("consolestreamoutput").data()); extractDataFromInfoBreak(str, handler->at(found)); - attemptBreakpointSynchronization(); // trigger "ready" } } } @@ -2286,15 +2298,13 @@ void GdbEngine::handleBreakInsert1(const GdbResponse &response) BreakpointData *data = handler->at(index); data->bpNumber = ""; } - attemptBreakpointSynchronization(); // trigger "ready" } void GdbEngine::attemptBreakpointSynchronization() { - //QTC_ASSERT(!m_breakListUpdating, - // qDebug() << "BREAK LIST CURRENTLY UPDATING"; return); QTC_ASSERT(!m_sourcesListUpdating, qDebug() << "SOURCES LIST CURRENTLY UPDATING"; return); + debugMessage(tr("ATTEMPT BREAKPOINT SYNC")); switch (state()) { case InferiorStarting: @@ -2332,6 +2342,7 @@ void GdbEngine::attemptBreakpointSynchronization() reloadBreakListInternal(); return; } + if (m_breakListOutdated) { reloadBreakListInternal(); return; @@ -2342,7 +2353,8 @@ void GdbEngine::attemptBreakpointSynchronization() foreach (BreakpointData *data, handler->takeDisabledBreakpoints()) { QByteArray bpNumber = data->bpNumber; if (!bpNumber.trimmed().isEmpty()) { - postCommand("-break-disable " + bpNumber, NeedsStop); + postCommand("-break-disable " + bpNumber, + NeedsStop | RebuildBreakpointModel); data->bpEnabled = false; } } @@ -2350,7 +2362,8 @@ void GdbEngine::attemptBreakpointSynchronization() foreach (BreakpointData *data, handler->takeEnabledBreakpoints()) { QByteArray bpNumber = data->bpNumber; if (!bpNumber.trimmed().isEmpty()) { - postCommand("-break-enable " + bpNumber, NeedsStop); + postCommand("-break-enable " + bpNumber, + NeedsStop | RebuildBreakpointModel); data->bpEnabled = true; } } @@ -2360,7 +2373,8 @@ void GdbEngine::attemptBreakpointSynchronization() debugMessage(_("DELETING BP " + bpNumber + " IN " + data->markerFileName.toLocal8Bit())); if (!bpNumber.trimmed().isEmpty()) - postCommand("-break-delete " + bpNumber, NeedsStop); + postCommand("-break-delete " + bpNumber, + NeedsStop | RebuildBreakpointModel); delete data; } @@ -2372,19 +2386,23 @@ void GdbEngine::attemptBreakpointSynchronization() } else if (data->bpNumber.toInt()) { if (data->bpMultiple && data->bpFileName.isEmpty()) { postCommand("info break " + data->bpNumber, + RebuildBreakpointModel, CB(handleBreakInfo), data->bpNumber.toInt()); continue; } // update conditions if needed if (data->condition != data->bpCondition && !data->conditionsMatch()) postCommand("condition " + data->bpNumber + ' ' + data->condition, - CB(handleBreakCondition), index); + RebuildBreakpointModel, + CB(handleBreakCondition), index); // update ignorecount if needed if (data->ignoreCount != data->bpIgnoreCount) postCommand("ignore " + data->bpNumber + ' ' + data->ignoreCount, - CB(handleBreakIgnore), index); + RebuildBreakpointModel, + CB(handleBreakIgnore), index); if (!data->enabled && data->bpEnabled) { - postCommand("-break-disable " + data->bpNumber, NeedsStop); + postCommand("-break-disable " + data->bpNumber, + NeedsStop | RebuildBreakpointModel); data->bpEnabled = false; } } @@ -3418,6 +3436,7 @@ void GdbEngine::handleChildren(const WatchData &data0, const GdbMi &item, void GdbEngine::updateLocals(const QVariant &cookie) { m_pendingWatchRequests = 0; + m_pendingBreakpointRequests = 0; if (hasPython()) updateLocalsPython(QByteArray()); else @@ -4121,6 +4140,7 @@ void GdbEngine::handleInferiorPrepared() // Initial attempt to set breakpoints showStatusMessage(tr("Setting breakpoints...")); + debugMessage(tr("Setting breakpoints...")); attemptBreakpointSynchronization(); if (m_cookieForToken.isEmpty()) { diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index c61d22e6b4e..59dd96a695c 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -102,7 +102,8 @@ private: ////////// General Interface ////////// virtual void addOptionPages(QList *opts) const; - virtual bool checkConfiguration(int toolChain, QString *errorMessage, QString *settingsPage= 0) const; + virtual bool checkConfiguration(int toolChain, QString *errorMessage, + QString *settingsPage = 0) const; virtual void startDebugger(const DebuggerStartParametersPtr &sp); virtual unsigned debuggerCapabilities() const; virtual void exitDebugger(); @@ -149,7 +150,8 @@ private slots: void readDebugeeOutput(const QByteArray &data); void handleAdapterStarted(); - void handleAdapterStartFailed(const QString &msg, const QString &settingsIdHint = QString()); + void handleAdapterStartFailed(const QString &msg, + const QString &settingsIdHint = QString()); void handleInferiorPrepared(); @@ -169,17 +171,26 @@ private: private: ////////// Gdb Command Management ////////// - public: // otherwise the Qt flag macros are unhappy + public: // Otherwise the Qt flag macros are unhappy. enum GdbCommandFlag { NoFlags = 0, - NeedsStop = 1, // The command needs a stopped inferior - Discardable = 2, // No need to wait for the reply before continuing inferior - RebuildWatchModel = 4, // Trigger model rebuild when no such commands are pending any more + // The command needs a stopped inferior. + NeedsStop = 1, + // No need to wait for the reply before continuing inferior. + Discardable = 2, + // Trigger watch model rebuild when no such commands are pending anymore. + RebuildWatchModel = 4, WatchUpdate = Discardable | RebuildWatchModel, - NonCriticalResponse = 8, // We can live without recieving an answer - RunRequest = 16, // Callback expects GdbResultRunning instead of GdbResultDone - ExitRequest = 32, // Callback expects GdbResultExit instead of GdbResultDone - LosesChild = 64 // Auto-set inferior shutdown related states + // We can live without recieving an answer. + NonCriticalResponse = 8, + // Callback expects GdbResultRunning instead of GdbResultDone. + RunRequest = 16, + // Callback expects GdbResultExit instead of GdbResultDone. + ExitRequest = 32, + // Auto-set inferior shutdown related states. + LosesChild = 64, + // Trigger breakpoint model rebuild when no such commands are pending anymore. + RebuildBreakpointModel = 128, }; Q_DECLARE_FLAGS(GdbCommandFlags, GdbCommandFlag) private: @@ -204,7 +215,7 @@ private: ////////// Gdb Command Management ////////// QTime postTime; }; - // type and cookie are sender-internal data, opaque for the "event + // Type and cookie are sender-internal data, opaque for the "event // queue". resultNeeded == true increments m_pendingResults on // send and decrements on receipt, effectively preventing // watch model updates before everything is finished. @@ -239,15 +250,16 @@ private: ////////// Gdb Command Management ////////// QByteArray m_pendingConsoleStreamOutput; QByteArray m_pendingLogStreamOutput; - // contains the first token number for the current round + // This contains the first token number for the current round // of evaluation. Responses with older tokens are considers // out of date and discarded. int m_oldestAcceptableToken; int m_pendingWatchRequests; // Watch updating commands in flight + int m_pendingBreakpointRequests; // Watch updating commands in flight typedef void (GdbEngine::*CommandsDoneCallback)(); - // function called after all previous responses have been received + // This function is called after all previous responses have been received. CommandsDoneCallback m_commandsDoneCallback; QList m_commandsToRunOnTemporaryBreak; @@ -402,7 +414,6 @@ private: ////////// View & Data Stuff ////////// bool m_sourcesListOutdated; bool m_sourcesListUpdating; bool m_breakListOutdated; - bool m_breakListUpdating; // // Stack specific stuff @@ -459,7 +470,6 @@ private: ////////// View & Data Stuff ////////// void handleVarCreate(const GdbResponse &response); void handleVarAssign(const GdbResponse &response); void handleEvaluateExpressionClassic(const GdbResponse &response); - //void handleToolTip(const GdbResponse &response); void handleQueryDebuggingHelperClassic(const GdbResponse &response); void handleDebuggingHelperValue2Classic(const GdbResponse &response); void handleDebuggingHelperValue3Classic(const GdbResponse &response); diff --git a/src/plugins/debugger/gdb/pythongdbengine.cpp b/src/plugins/debugger/gdb/pythongdbengine.cpp index 64a8b860cdb..e5b147d9afb 100644 --- a/src/plugins/debugger/gdb/pythongdbengine.cpp +++ b/src/plugins/debugger/gdb/pythongdbengine.cpp @@ -142,6 +142,7 @@ void GdbEngine::handleStackFramePython(const GdbResponse &response) //for (int i = 0; i != list.size(); ++i) // qDebug() << "LOCAL: " << list.at(i).toString(); +#if 0 data = all.findChild("bkpts"); if (data.isValid()) { BreakHandler *handler = manager()->breakHandler(); @@ -175,6 +176,7 @@ void GdbEngine::handleStackFramePython(const GdbResponse &response) } handler->updateMarkers(); } +#endif //PENDING_DEBUG("AFTER handleStackFrame()"); // FIXME: This should only be used when updateLocals() was