diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp index 4f281291acea7d5a36788697d3a718918f92aaa0..1230198b38abd92bacaa967ea82defa8989b8c6e 100644 --- a/src/plugins/debugger/breakhandler.cpp +++ b/src/plugins/debugger/breakhandler.cpp @@ -177,6 +177,13 @@ BreakpointId BreakHandler::findBreakpointByFileAndLine(const QString &fileName, return BreakpointId(-1); } +const BreakpointData *BreakHandler::breakpointById(BreakpointId id) const +{ + ConstIterator it = m_storage.find(id); + QTC_ASSERT(it != m_storage.end(), return 0); + return &it->data; +} + BreakpointData *BreakHandler::breakpointById(BreakpointId id) { Iterator it = m_storage.find(id); diff --git a/src/plugins/debugger/breakhandler.h b/src/plugins/debugger/breakhandler.h index cc027636be272f255c27a86ed89124bf52ec3ceb..0bd8b27e3e111c1c3d61029497220dea50209177 100644 --- a/src/plugins/debugger/breakhandler.h +++ b/src/plugins/debugger/breakhandler.h @@ -89,6 +89,8 @@ public: BreakpointId findBreakpointByFileAndLine(const QString &fileName, int lineNumber, bool useMarkerPosition = true); BreakpointId findBreakpointByAddress(quint64 address) const; + const BreakpointData *breakpointById(BreakpointId id) const; + BreakpointData *breakpointById(BreakpointId id); // FIXME: For breakwindow. void breakByFunction(const QString &functionName); void removeBreakpoint(BreakpointId id); @@ -145,9 +147,6 @@ public: private: friend class BreakpointMarker; - friend class BreakWindow; // FIXME: remove. - BreakpointData *breakpointById(BreakpointId id); - // QAbstractItemModel int columnCount(const QModelIndex &parent) const; int rowCount(const QModelIndex &parent) const; diff --git a/src/plugins/debugger/breakwindow.cpp b/src/plugins/debugger/breakwindow.cpp index d7993fc3300b86835b18013d1400887ae6e24eb8..8551ad920b8e3ad4c06afd3baee5e55dabef3a6c 100644 --- a/src/plugins/debugger/breakwindow.cpp +++ b/src/plugins/debugger/breakwindow.cpp @@ -364,11 +364,13 @@ void BreakWindow::contextMenuEvent(QContextMenuEvent *ev) addBreakpoint(); else if (act == breakAtThrowAction) { BreakpointData data; + data.setType(BreakpointByFunction); data.setFunctionName(BreakpointData::throwFunction); handler->appendBreakpoint(data); } else if (act == breakAtCatchAction) { // FIXME: Use the proper breakpoint type instead. BreakpointData data; + data.setType(BreakpointByFunction); data.setFunctionName(BreakpointData::catchFunction); handler->appendBreakpoint(data); } diff --git a/src/plugins/debugger/cdb/cdbbreakpoint.cpp b/src/plugins/debugger/cdb/cdbbreakpoint.cpp index 154fd37c6f3986c642726ab51635c2c005ae2edd..1d701d0d5d7460c268864e6002eaf98d282b8fee 100644 --- a/src/plugins/debugger/cdb/cdbbreakpoint.cpp +++ b/src/plugins/debugger/cdb/cdbbreakpoint.cpp @@ -28,38 +28,39 @@ **************************************************************************/ #include "cdbbreakpoint.h" +#include "cdbengine_p.h" +#include "corebreakpoint.h" #include "cdbmodules.h" +#include "breakhandler.h" #include <QtCore/QDebug> +#include <QtCore/QDir> namespace Debugger { namespace Internal { -enum { debugBP = 0 }; - // Convert breakpoint structs -CdbCore::BreakPoint breakPointFromBreakPointData(const BreakpointData &bpd) +static CdbCore::BreakPoint breakPointFromBreakPointData(const BreakpointData &bpd, const QString &functionName) { CdbCore::BreakPoint rc; - rc.type = bpd.type == Watchpoint ? + rc.type = bpd.type() == Watchpoint ? CdbCore::BreakPoint::Data : CdbCore::BreakPoint::Code ; - rc.address = bpd.address; - if (!bpd.threadSpec.isEmpty()) { + rc.address = bpd.address(); + if (!bpd.threadSpec().isEmpty()) { bool ok; - rc.threadId = bpd.threadSpec.toInt(&ok); + rc.threadId = bpd.threadSpec().toInt(&ok); if (!ok) - qWarning("Cdb: Cannot convert breakpoint thread specification '%s'", bpd.threadSpec.constData()); + qWarning("Cdb: Cannot convert breakpoint thread specification '%s'", bpd.threadSpec().constData()); } - rc.fileName = QDir::toNativeSeparators(bpd.fileName); - rc.condition = bpd.condition; - // Resolved function goes to bpd.bpFuncName. - rc.funcName = bpd.bpFuncName.isEmpty() ? bpd.funcName : bpd.bpFuncName; - rc.ignoreCount = bpd.ignoreCount; - rc.lineNumber = bpd.lineNumber; + rc.fileName = QDir::toNativeSeparators(bpd.fileName()); + rc.condition = bpd.condition(); + rc.funcName = functionName.isEmpty() ? bpd.functionName() : functionName; + rc.ignoreCount = bpd.ignoreCount(); + rc.lineNumber = bpd.lineNumber(); rc.oneShot = false; - rc.enabled = bpd.enabled; + rc.enabled = bpd.isEnabled(); return rc; } @@ -68,56 +69,63 @@ static inline QString msgCannotSetBreakAtFunction(const QString &func, const QSt return QString::fromLatin1("Cannot set a breakpoint at '%1': %2").arg(func, why); } -static bool addBreakpoint(CIDebugControl* debugControl, - CIDebugSymbols *syms, - BreakpointData *nbd, - QString *warning) +void setBreakpointResponse(const BreakpointData *nbd, int number, BreakpointResponse *response) +{ + response->bpAddress = nbd->address(); + response->bpNumber = number; + response->bpFuncName = nbd->functionName(); + response->bpType = nbd->type(); + response->bpCondition = nbd->condition(); + response->bpIgnoreCount = nbd->ignoreCount(); + response->bpFullName = response->bpFileName = nbd->fileName(); + response->bpLineNumber = nbd->lineNumber(); + response->bpThreadSpec = nbd->threadSpec(); + response->bpEnabled = nbd->isEnabled(); +} + +bool addCdbBreakpoint(CIDebugControl* debugControl, + CIDebugSymbols *syms, + const BreakpointData *nbd, + BreakpointResponse *response, + QString *errorMessage) { - warning->clear(); + errorMessage->clear(); // Function breakpoints: Are the module names specified? - if (!nbd->funcName.isEmpty()) { - nbd->bpFuncName = nbd->funcName; - switch (resolveSymbol(syms, &nbd->bpFuncName, warning)) { + QString resolvedFunction; + if (nbd->type() == BreakpointByFunction) { + resolvedFunction = nbd->functionName(); + switch (resolveSymbol(syms, &resolvedFunction, errorMessage)) { case ResolveSymbolOk: break; case ResolveSymbolAmbiguous: - *warning = msgCannotSetBreakAtFunction(nbd->funcName, *warning); break; case ResolveSymbolNotFound: case ResolveSymbolError: - *warning = msgCannotSetBreakAtFunction(nbd->funcName, *warning); + *errorMessage = msgCannotSetBreakAtFunction(nbd->functionName(), *errorMessage); return false; - }; + } + if (debugBreakpoints) + qDebug() << nbd->functionName() << " resolved to " << resolvedFunction; } // function breakpoint // Now add... quint64 address; unsigned long id; - const CdbCore::BreakPoint ncdbbp = breakPointFromBreakPointData(*nbd); - if (!ncdbbp.add(debugControl, warning, &id, &address)) + const CdbCore::BreakPoint ncdbbp = breakPointFromBreakPointData(*nbd, resolvedFunction); + if (!ncdbbp.add(debugControl, errorMessage, &id, &address)) return false; - if (debugBP) + if (debugBreakpoints) qDebug("Added %lu at 0x%lx %s", id, address, qPrintable(ncdbbp.toString())); - nbd->pending = false; - nbd->bpNumber = QByteArray::number(uint(id)); - nbd->bpAddress = address; - // Take over rest as is - nbd->bpCondition = nbd->condition; - nbd->bpIgnoreCount = nbd->ignoreCount; - nbd->bpThreadSpec = nbd->threadSpec; - nbd->bpFileName = nbd->fileName; - nbd->bpLineNumber = nbd->lineNumber; + setBreakpointResponse(nbd, id, response); + response->bpAddress = address; + response->bpFuncName = resolvedFunction; return true; } -// Synchronize (halted) engine breakpoints with those of the BreakHandler. -bool synchronizeBreakPoints(CIDebugControl* debugControl, - CIDebugSymbols *syms, - BreakHandler *handler, - QString *errorMessage, - QStringList *warnings) +// Delete all breakpoints +bool deleteCdbBreakpoints(CIDebugControl* debugControl, + QString *errorMessage) { errorMessage->clear(); - warnings->clear(); // Do an initial check whether we are in a state that allows // for modifying breakPoints ULONG engineCount; @@ -125,50 +133,28 @@ bool synchronizeBreakPoints(CIDebugControl* debugControl, *errorMessage = QString::fromLatin1("Cannot modify breakpoints: %1").arg(*errorMessage); return false; } - // Delete all breakpoints and re-insert all enabled breakpoints. This is the simplest - // way to apply changes since CDB ids shift when removing breakpoints and there is no - // easy way to re-match them. + if (debugBreakpoints) + qDebug("Deleting breakpoints 0..%lu", engineCount); + if (engineCount) { for (int b = engineCount - 1; b >= 0 ; b--) if (!CdbCore::BreakPoint::removeBreakPointById(debugControl, b, errorMessage)) return false; } - qDeleteAll(handler->takeRemovedBreakpoints()); - // Mark disabled ones - foreach(BreakpointData *dbd, handler->takeDisabledBreakpoints()) - dbd->bpEnabled = false; - - // Insert all enabled ones as new - QString warning; - bool updateMarkers = false; - const int size = handler->size(); - for (int i = 0; i < size; i++) { - BreakpointData *breakpoint = handler->at(i); - if (breakpoint->enabled) - if (addBreakpoint(debugControl, syms, breakpoint, &warning)) { - updateMarkers = true; - } else { - warnings->push_back(warning); - } - } - // Mark enabled ones - foreach(BreakpointData *ebd, handler->takeEnabledBreakpoints()) - ebd->bpEnabled = true; - - if (updateMarkers) - handler->updateMarkers(); - - if (debugBP > 1) { - QList<CdbCore::BreakPoint> bps; - CdbCore::BreakPoint::getBreakPoints(debugControl, &bps, errorMessage); - QDebug nsp = qDebug().nospace(); - const int count = bps.size(); - nsp <<"### Breakpoints in engine: " << count << '\n'; - for (int i = 0; i < count; i++) - nsp << " #" << i << ' ' << bps.at(i) << '\n'; - } return true; } +void debugCdbBreakpoints(CIDebugControl* debugControl) +{ + QString errorMessage; + QList<CdbCore::BreakPoint> bps; + CdbCore::BreakPoint::getBreakPoints(debugControl, &bps, &errorMessage); + QDebug nsp = qDebug().nospace(); + const int count = bps.size(); + nsp <<"### Breakpoints in engine: " << count << '\n'; + for (int i = 0; i < count; i++) + nsp << " #" << i << ' ' << bps.at(i) << '\n'; +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbbreakpoint.h b/src/plugins/debugger/cdb/cdbbreakpoint.h index 14a48d6c07aee187931395bad498febd44d5b068..3a2d1ea626e86eebb37f7ea0542b38139483b03f 100644 --- a/src/plugins/debugger/cdb/cdbbreakpoint.h +++ b/src/plugins/debugger/cdb/cdbbreakpoint.h @@ -31,12 +31,8 @@ #define CDBBREAKPOINTS_H #include "cdbcom.h" -#include "corebreakpoint.h" -#include "breakhandler.h" #include <QtCore/QString> -#include <QtCore/QList> -#include <QtCore/QDir> QT_BEGIN_NAMESPACE class QDebug; @@ -44,14 +40,22 @@ QT_END_NAMESPACE namespace Debugger { namespace Internal { +class BreakpointData; +class BreakpointResponse; // Convert breakpoint structs -CdbCore::BreakPoint breakPointFromBreakPointData(const Debugger::Internal::BreakpointData &bpd); +bool addCdbBreakpoint(CIDebugControl* debugControl, + CIDebugSymbols *syms, + const BreakpointData *nbd, + BreakpointResponse *response, + QString *errorMessage); -// Synchronize (halted) engine with BreakHandler. -bool synchronizeBreakPoints(CIDebugControl* ctl, CIDebugSymbols *syms, - BreakHandler *bh, - QString *errorMessage, QStringList *warnings); +bool deleteCdbBreakpoints(CIDebugControl* debugControl, QString *errorMessage); + +void debugCdbBreakpoints(CIDebugControl* debugControl); + +// Set response from data. +void setBreakpointResponse(const BreakpointData *nbd, int number, BreakpointResponse *response); } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index e70b0b2a6ba76149bf2edd30684a08a27455a43d..f99eed5381b08976691841134c52bcdd226f5ecb 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -52,6 +52,7 @@ #include "registerhandler.h" #include "moduleshandler.h" #include "watchutils.h" +#include "corebreakpoint.h" #include <coreplugin/icore.h> #include <utils/qtcassert.h> @@ -1223,21 +1224,61 @@ void CdbEngine::attemptBreakpointSynchronization() if (!m_d->m_hDebuggeeProcess) // Sometimes called from the breakpoint Window return; QString errorMessage; - if (!m_d->attemptBreakpointSynchronization(&errorMessage)) + if (!attemptBreakpointSynchronizationI(&errorMessage)) warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage)); } -bool CdbEnginePrivate::attemptBreakpointSynchronization(QString *errorMessage) +// Figure out what kind of changes are required to synchronize +enum BreakPointSyncType { + BreakpointsUnchanged, BreakpointsAdded, BreakpointsRemovedChanged +}; + +static inline BreakPointSyncType breakPointSyncType(const BreakHandler *handler, const BreakpointIds ids) +{ + bool added = false; + foreach (BreakpointId id, ids) { + switch (handler->state(id)) { + case BreakpointInsertRequested: + added = true; + break; + case BreakpointChangeRequested: + case BreakpointRemoveRequested: + return BreakpointsRemovedChanged; + default: + break; + } + } + return added ? BreakpointsAdded : BreakpointsUnchanged; +} + +bool CdbEngine::attemptBreakpointSynchronizationI(QString *errorMessage) { - if (!m_hDebuggeeProcess) { + + if (!m_d->m_hDebuggeeProcess) { *errorMessage = QLatin1String("attemptBreakpointSynchronization() called while debugger is not running"); return false; } // Might be called nested while attempting to stop. - if (m_breakEventMode == BreakEventSyncBreakPoints) { + if (m_d->m_breakEventMode == CdbEnginePrivate::BreakEventSyncBreakPoints) { *errorMessage = QLatin1String("Nested invocation of attemptBreakpointSynchronization."); return false; } + + // Check if there is anything to be done at all. + BreakHandler *handler = breakHandler(); + // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only? + foreach (BreakpointId id, handler->unclaimedBreakpointIds()) + if (acceptsBreakpoint(id)) + handler->setEngine(id, this); + + // Find out if there is a need to synchronize again + const BreakpointIds ids = handler->engineBreakpointIds(this); + const BreakPointSyncType syncType = breakPointSyncType(handler, ids); + if (debugBreakpoints) + qDebug("attemptBreakpointSynchronizationI %d breakpoints, syncType=%d", ids.size(), syncType); + if (syncType == BreakpointsUnchanged) + return true; + // This is called from // 1) CreateProcessEvent with the halted engine // 2) from the break handler, potentially while the debuggee is running @@ -1245,30 +1286,78 @@ bool CdbEnginePrivate::attemptBreakpointSynchronization(QString *errorMessage) // no reliable indicator), we temporarily halt and have ourselves // called again from the debug event handler. + CIDebugControl *control = m_d->interfaces().debugControl; + CIDebugSymbols *symbols = m_d->interfaces().debugSymbols; + ULONG dummy; - const bool wasRunning = !CdbCore::BreakPoint::getBreakPointCount(interfaces().debugControl, &dummy); + const bool wasRunning = !CdbCore::BreakPoint::getBreakPointCount(control, &dummy); if (debugCDB) qDebug() << Q_FUNC_INFO << "\n Running=" << wasRunning; if (wasRunning) { - const HandleBreakEventMode oldMode = m_breakEventMode; - m_breakEventMode = BreakEventSyncBreakPoints; - if (!interruptInterferiorProcess(errorMessage)) { - m_breakEventMode = oldMode; + const CdbEnginePrivate::HandleBreakEventMode oldMode = m_d->m_breakEventMode; + m_d->m_breakEventMode = CdbEnginePrivate::BreakEventSyncBreakPoints; + if (!m_d->interruptInterferiorProcess(errorMessage)) { + m_d->m_breakEventMode = oldMode; return false; } return true; } - QStringList warnings; - const bool ok = synchronizeBreakPoints(interfaces().debugControl, - interfaces().debugSymbols, - m_engine->breakHandler(), - errorMessage, &warnings); - if (const int warningsCount = warnings.size()) - for (int w = 0; w < warningsCount; w++) - m_engine->warning(warnings.at(w)); - return ok; + // If there are changes/removals, delete all breakpoints and re-insert + // all enabled breakpoints. This is the simplest + // way to apply changes since CDB ids shift when removing breakpoints and there is no + // easy way to re-match them. + if (syncType == BreakpointsRemovedChanged && !deleteCdbBreakpoints(control, errorMessage)) + return false; + + foreach (BreakpointId id, ids) { + BreakpointResponse response; + const BreakpointData *data = handler->breakpointById(id); + errorMessage->clear(); + switch (handler->state(id)) { + case BreakpointInsertRequested: + handler->setState(id, BreakpointInsertProceeding); + if (addCdbBreakpoint(control, symbols, data, &response, errorMessage)) { + notifyBreakpointInsertOk(id); + handler->setResponse(id, response); + } else { + notifyBreakpointInsertOk(id); + showMessage(*errorMessage, LogError); + } + break; + case BreakpointChangeRequested: + // Skip disabled breakpoints, else add + handler->setState(id, BreakpointChangeProceeding); + if (data->isEnabled()) { + if (addCdbBreakpoint(control, symbols, data, &response, errorMessage)) { + notifyBreakpointChangeOk(id); + handler->setResponse(id, response); + } else { + notifyBreakpointChangeFailed(id); + showMessage(*errorMessage, LogError); + } + } else { + notifyBreakpointChangeOk(id); + } + break; + case BreakpointRemoveRequested: + notifyBreakpointRemoveOk(id); + break; + case BreakpointInserted: + case BreakpointPending: + // Existing breakpoints were deleted due to change/removal, re-set + if (syncType == BreakpointsRemovedChanged + && !addCdbBreakpoint(control, symbols, handler->breakpointById(id), &response, errorMessage)) + showMessage(*errorMessage, LogError); + break; + default: + break; + } + } + if (debugBreakpoints) + debugCdbBreakpoints(control); + return true; } void CdbEngine::fetchDisassembler(DisassemblerViewAgent *agent) @@ -1493,7 +1582,7 @@ void CdbEnginePrivate::handleDebugEvent() // Temp stop to sync breakpoints (without invoking states). // Triggered when the users changes breakpoints while running. QString errorMessage; - attemptBreakpointSynchronization(&errorMessage); + m_engine->attemptBreakpointSynchronizationI(&errorMessage); startWatchTimer(); if (!continueInferiorProcess(&errorMessage)) { STATE_DEBUG(Q_FUNC_INFO, __LINE__, "BreakEventSyncBreakPoints / notifyInferiorSpontaneousStop"); diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index 55ab588d23094517da4c9711ce6d8cb1269f8b5f..32ecbc57ea7e2d45eb1642adc0a96561391d2a48 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -115,6 +115,7 @@ private: void evaluateWatcher(WatchData *wd); QString editorToolTip(const QString &exp, const QString &function); bool step(unsigned long executionStatus); + bool attemptBreakpointSynchronizationI(QString *errorMessage); CdbEnginePrivate *m_d; diff --git a/src/plugins/debugger/cdb/cdbengine_p.h b/src/plugins/debugger/cdb/cdbengine_p.h index 3608ba74e6f64240cb352a0e2005b9b52b005f59..71bbe0a7d4052fe5b3a99afdc74a8cc481187ead 100644 --- a/src/plugins/debugger/cdb/cdbengine_p.h +++ b/src/plugins/debugger/cdb/cdbengine_p.h @@ -95,7 +95,6 @@ public: bool continueInferior(QString *errorMessage); bool executeContinueCommand(const QString &command); - bool attemptBreakpointSynchronization(QString *errorMessage); void notifyException(long code, bool fatal, const QString &message); @@ -144,6 +143,7 @@ enum { debugCDB = 0 }; enum { debugCDBExecution = 0 }; enum { debugCDBWatchHandling = 0 }; enum { debugToolTips = 0 }; +enum { debugBreakpoints = 0 }; } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp index 0d6923555ed5beef91d43dec52e22dd6450dd4be..4bffd8e2e9c6df0b7943f81f0895f0bda26b579f 100644 --- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp +++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp @@ -34,6 +34,7 @@ #include "watchutils.h" #include "debuggeractions.h" #include "coreengine.h" +#include "debuggercore.h" #include <QtCore/QDebug> #include <QtCore/QTextStream>