diff --git a/src/plugins/debugger/cdb/breakpoint.cpp b/src/plugins/debugger/cdb/breakpoint.cpp index 469d39e5835..13aacaf8cfb 100644 --- a/src/plugins/debugger/cdb/breakpoint.cpp +++ b/src/plugins/debugger/cdb/breakpoint.cpp @@ -50,48 +50,21 @@ namespace CdbCore { static const char sourceFileQuoteC = '`'; BreakPoint::BreakPoint() : + type(Code), lineNumber(-1), address(0), + threadId(-1), ignoreCount(0), oneShot(false), enabled(true) { } -int BreakPoint::compare(const BreakPoint& rhs) const -{ - if (lineNumber > rhs.lineNumber) - return 1; - if (lineNumber < rhs.lineNumber) - return -1; - if (address > rhs.address) - return 1; - if (address < rhs.address) - return -1; - if (ignoreCount > rhs.ignoreCount) - return 1; - if (ignoreCount < rhs.ignoreCount) - return -1; - if (oneShot && !rhs.oneShot) - return 1; - if (!oneShot && rhs.oneShot) - return -1; - if (enabled && !rhs.enabled) - return 1; - if (!enabled && rhs.enabled) - return -1; - if (const int fileCmp = fileName.compare(rhs.fileName)) - return fileCmp; - if (const int funcCmp = funcName.compare(rhs.funcName)) - return funcCmp; - if (const int condCmp = condition.compare(rhs.condition)) - return condCmp; - return 0; -} - void BreakPoint::clear() { + type = Code; ignoreCount = 0; + threadId = -1; oneShot = false; enabled = true; clearExpressionData(); @@ -108,25 +81,37 @@ void BreakPoint::clearExpressionData() QDebug operator<<(QDebug dbg, const BreakPoint &bp) { - QDebug nsp = dbg.nospace(); - if (bp.address) - nsp << "0x" << QString::number(bp.address, 16) << ' '; - if (!bp.fileName.isEmpty()) { - nsp << "fileName='" << bp.fileName << ':' << bp.lineNumber << '\''; - } else { - nsp << "funcName='" << bp.funcName << '\''; - } - if (!bp.condition.isEmpty()) - nsp << " condition='" << bp.condition << '\''; - if (bp.ignoreCount) - nsp << " ignoreCount=" << bp.ignoreCount; - if (bp.enabled) - nsp << " enabled"; - if (bp.oneShot) - nsp << " oneShot"; + dbg.nospace() << bp.toString(); return dbg; } +QString BreakPoint::toString() const +{ + QString rc; + QTextStream str(&rc); + str << (type == BreakPoint::Code ? "Code " : "Data "); + if (address) { + str.setIntegerBase(16); + str << "0x" << address << ' '; + str.setIntegerBase(10); + } + if (!fileName.isEmpty()) { + str << "fileName='" << fileName << ':' << lineNumber << '\''; + } else { + str << "funcName='" << funcName << '\''; + } + if (threadId >= 0) + str << " thread=" << threadId; + if (!condition.isEmpty()) + str << " condition='" << condition << '\''; + if (ignoreCount) + str << " ignoreCount=" << ignoreCount; + str << (enabled ? " enabled" : " disabled"); + if (oneShot) + str << " oneShot"; + return rc; +} + QString BreakPoint::expression() const { // format the breakpoint expression (file/function and condition) @@ -155,25 +140,59 @@ QString BreakPoint::expression() const return rc; } +static inline QString msgCannotSetBreakpoint(const QString &exp, const QString &why) +{ + return QString::fromLatin1("Unable to set breakpoint '%1' : %2").arg(exp, why); +} + bool BreakPoint::apply(CIDebugBreakpoint *ibp, QString *errorMessage) const { const QString expr = expression(); if (debugBP) qDebug() << Q_FUNC_INFO << *this << expr; - const HRESULT hr = ibp->SetOffsetExpressionWide(reinterpret_cast(expr.utf16())); + HRESULT hr = ibp->SetOffsetExpressionWide(reinterpret_cast(expr.utf16())); if (FAILED(hr)) { - *errorMessage = QString::fromLatin1("Unable to set breakpoint '%1' : %2"). - arg(expr, CdbCore::msgComFailed("SetOffsetExpressionWide", hr)); + *errorMessage = msgCannotSetBreakpoint(expr, CdbCore::msgComFailed("SetOffsetExpressionWide", hr)); return false; } // Pass Count is ignoreCount + 1 - ibp->SetPassCount(ignoreCount + 1u); + hr = ibp->SetPassCount(ignoreCount + 1u); + if (FAILED(hr)) + qWarning("Error setting passcount %d %s ", ignoreCount, qPrintable(expr)); + // Set up size for data breakpoints + if (type == Data) { + const ULONG size = 1u; + hr = ibp->SetDataParameters(size, DEBUG_BREAK_READ | DEBUG_BREAK_WRITE); + if (FAILED(hr)) { + const QString msg = QString::fromLatin1("Cannot set watch size to %1: %2"). + arg(size).arg(CdbCore::msgComFailed("SetDataParameters", hr)); + *errorMessage = msgCannotSetBreakpoint(expr, msg); + return false; + } + } + // Thread + if (threadId >= 0) { + hr = ibp->SetMatchThreadId(threadId); + if (FAILED(hr)) { + const QString msg = QString::fromLatin1("Cannot set thread id to %1: %2"). + arg(threadId).arg(CdbCore::msgComFailed("SetMatchThreadId", hr)); + *errorMessage = msgCannotSetBreakpoint(expr, msg); + return false; + } + } + // Flags ULONG flags = 0; if (enabled) flags |= DEBUG_BREAKPOINT_ENABLED; if (oneShot) flags |= DEBUG_BREAKPOINT_ONE_SHOT; - ibp->AddFlags(flags); + hr = ibp->AddFlags(flags); + if (FAILED(hr)) { + const QString msg = QString::fromLatin1("Cannot set flags to 0x%1: %2"). + arg(flags, 0 ,16).arg(CdbCore::msgComFailed("AddFlags", hr)); + *errorMessage = msgCannotSetBreakpoint(expr, msg); + return false; + } return true; } @@ -192,7 +211,8 @@ bool BreakPoint::add(CIDebugControl* debugControl, *address = 0; if (id) *id = 0; - HRESULT hr = debugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &ibp); + const ULONG iType = type == Code ? DEBUG_BREAKPOINT_CODE : DEBUG_BREAKPOINT_DATA; + HRESULT hr = debugControl->AddBreakpoint2(iType, DEBUG_ANY_ID, &ibp); if (FAILED(hr)) { *errorMessage = msgCannotAddBreakPoint(CdbCore::msgComFailed("AddBreakpoint2", hr)); return false; @@ -321,16 +341,41 @@ void BreakPoint::clearNormalizeFileNameCache() normalizedFileNameCache()->clear(); } +static inline QString msgCannotRetrieveBreakpoint(const QString &why) +{ + return QString::fromLatin1("Cannot retrieve breakpoint: %1").arg(why); +} + +static inline int threadIdOfBreakpoint(CIDebugBreakpoint *ibp) +{ + // Thread: E_NOINTERFACE indicates no thread has been set. + int threadId = -1; + ULONG iThreadId; + if (S_OK == ibp->GetMatchThreadId(&iThreadId)) + threadId = iThreadId; + return threadId; +} + bool BreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage) { clear(); - WCHAR wszBuf[MAX_PATH]; - const HRESULT hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0); + // Get type + ULONG iType; + ULONG processorType; + HRESULT hr = ibp->GetType(&iType, &processorType); if (FAILED(hr)) { - *errorMessage = QString::fromLatin1("Cannot retrieve breakpoint: %1"). - arg(CdbCore::msgComFailed("GetOffsetExpressionWide", hr)); + *errorMessage = msgCannotRetrieveBreakpoint(CdbCore::msgComFailed("GetType", hr)); return false; } + type = iType == DEBUG_BREAKPOINT_CODE ? Code : Data; + // Get & parse expression + WCHAR wszBuf[MAX_PATH]; + hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0); + if (FAILED(hr)) { + *errorMessage = msgCannotRetrieveBreakpoint(CdbCore::msgComFailed("GetOffsetExpressionWide", hr)); + return false; + } + threadId = threadIdOfBreakpoint(ibp); // Pass Count is ignoreCount + 1 ibp->GetPassCount(&ignoreCount); if (ignoreCount) @@ -523,4 +568,34 @@ bool BreakPoint::setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id, return true; } +// Change thread-id of a breakpoint +static inline QString msgCannotSetBreakPointThread(unsigned long id, int tid, const QString &why) +{ + return QString::fromLatin1("Cannot set breakpoint %1 thread to %2: %3").arg(id).arg(tid).arg(why); } + +bool BreakPoint::setBreakPointThreadById(CIDebugControl *ctl, unsigned long id, int threadId, QString *errorMessage) +{ + if (debugBP) + qDebug() << Q_FUNC_INFO << id << threadId; + CIDebugBreakpoint *ibp = breakPointById(ctl, id, errorMessage); + if (!ibp) { + *errorMessage = msgCannotSetBreakPointThread(id, threadId, *errorMessage); + return false; + } + // Compare thread ids + const int oldThreadId = threadIdOfBreakpoint(ibp); + if (oldThreadId == threadId) + return true; + const ULONG newIThreadId = threadId == -1 ? DEBUG_ANY_ID : static_cast(threadId); + if (debugBP) + qDebug() << "Changing thread id of " << id << " from " << oldThreadId << " to " << threadId + << '(' << newIThreadId << ')'; + const HRESULT hr = ibp->SetMatchThreadId(newIThreadId); + if (FAILED(hr)) { + *errorMessage = msgCannotSetBreakPointThread(id, threadId, *errorMessage); + return false; + } + return true; +} +} // namespace CdbCore diff --git a/src/plugins/debugger/cdb/breakpoint.h b/src/plugins/debugger/cdb/breakpoint.h index 94d3b52e2f0..515243ccc92 100644 --- a/src/plugins/debugger/cdb/breakpoint.h +++ b/src/plugins/debugger/cdb/breakpoint.h @@ -49,18 +49,21 @@ namespace CdbCore { struct BreakPoint { - BreakPoint(); + enum Type { Code, // Stop in code. + Data // Stop when accessing address. + }; - int compare(const BreakPoint& rhs) const; + BreakPoint(); void clear(); void clearExpressionData(); QString expression() const; - // Apply parameters + // Apply parameters (with the exception of type, which is + // passed as a parameter to IDebugControl within add(). bool apply(IDebugBreakpoint2 *ibp, QString *errorMessage) const; - // Convenience to add to a IDebugControl4 + // Convenience to add to a IDebugControl4. bool add(CIDebugControl* debugControl, QString *errorMessage, unsigned long *id = 0, @@ -76,16 +79,20 @@ struct BreakPoint static IDebugBreakpoint2 *breakPointById(CIDebugControl *ctl, unsigned long id, QString *errorMessage); static bool removeBreakPointById(CIDebugControl *ctl, unsigned long id, QString *errorMessage); static bool setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id, bool enabled, QString *errorMessage); + static bool setBreakPointThreadById(CIDebugControl *ctl, unsigned long id, int threadId, QString *errorMessage); // Return a 'canonical' file (using '/' and capitalized drive letter) static QString normalizeFileName(const QString &f); static void clearNormalizeFileNameCache(); + QString toString() const; + Type type; QString fileName; // short name of source file int lineNumber; // line in source file QString funcName; // name of containing function quint64 address; + int threadId; QString condition; // condition associated with breakpoint unsigned long ignoreCount; // ignore count associated with breakpoint bool oneShot; @@ -94,10 +101,6 @@ struct BreakPoint QDebug operator<<(QDebug, const BreakPoint &bp); -inline bool operator==(const BreakPoint& b1, const BreakPoint& b2) - { return b1.compare(b2) == 0; } -inline bool operator!=(const BreakPoint& b1, const BreakPoint& b2) - { return b1.compare(b2) != 0; } -} +} // namespace CdbCore #endif // CDBCOREBREAKPOINTS_H diff --git a/src/plugins/debugger/cdb/cdbbreakpoint.cpp b/src/plugins/debugger/cdb/cdbbreakpoint.cpp index cdd57b5544b..6c4898e6eac 100644 --- a/src/plugins/debugger/cdb/cdbbreakpoint.cpp +++ b/src/plugins/debugger/cdb/cdbbreakpoint.cpp @@ -37,6 +37,37 @@ namespace Internal { enum { debugBP = 0 }; +// Convert breakpoint structs +CdbCore::BreakPoint breakPointFromBreakPointData(const Debugger::Internal::BreakpointData &bpd) +{ + CdbCore::BreakPoint rc; + rc.type = bpd.type == Debugger::Internal::BreakpointData::BreakpointType ? + CdbCore::BreakPoint::Code : CdbCore::BreakPoint::Data; + + if (rc.type == CdbCore::BreakPoint::Data) { + QByteArray addressBA = bpd.address; + if (addressBA.startsWith("0x")) + addressBA.remove(0, 2); + bool ok; + rc.address = addressBA.toULongLong(&ok, 16); + if (!ok) + qWarning("Cdb: Cannot convert watchpoint address '%s'", bpd.address.constData()); + } + if (!bpd.threadSpec.isEmpty()) { + bool ok; + rc.threadId = bpd.threadSpec.toInt(&ok); + if (!ok) + qWarning("Cdb: Cannot convert breakpoint thread specification '%s'", bpd.address.constData()); + } + rc.fileName = QDir::toNativeSeparators(bpd.fileName); + rc.condition = bpd.condition; + rc.funcName = bpd.funcName; + rc.ignoreCount = bpd.ignoreCount.isEmpty() ? 0 : bpd.ignoreCount.toInt(); + rc.lineNumber = bpd.lineNumber.isEmpty() ? -1 : bpd.lineNumber.toInt(); + rc.oneShot = false; + rc.enabled = bpd.enabled; + return rc; +} static inline QString msgCannotSetBreakAtFunction(const QString &func, const QString &why) { @@ -93,7 +124,7 @@ bool synchronizeBreakPoints(CIDebugControl* debugControl, breakPointOk = ncdbbp.add(debugControl, &warning, &id, &address); if (breakPointOk) { if (debugBP) - qDebug() << "Added " << id << " at " << address << ncdbbp; + qDebug("Added %lu at 0x%lx %s", id, address, qPrintable(ncdbbp.toString())); handler->takeInsertedBreakPoint(nbd); updateMarkers = true; nbd->pending = false; @@ -123,6 +154,18 @@ bool synchronizeBreakPoints(CIDebugControl* debugControl, foreach (BreakpointData *dbd, handler->takeDisabledBreakpoints()) if (!CdbCore::BreakPoint::setBreakPointEnabledById(debugControl, dbd->bpNumber.toUInt(), false, &warning)) warnings->push_back(warning); + // Check for modified thread ids. + for (int i = handler->size() - 1; i >= 0; i--) { + BreakpointData *bpd = handler->at(i); + if (bpd->threadSpec != bpd->bpThreadSpec) { + const int newThreadSpec = bpd->threadSpec.isEmpty() ? -1 : bpd->threadSpec.toInt(); + if (CdbCore::BreakPoint::setBreakPointThreadById(debugControl, bpd->bpNumber.toUInt(), newThreadSpec, errorMessage)) { + bpd->bpThreadSpec = bpd->threadSpec; + } else { + qWarning("%s", qPrintable(*errorMessage)); + } + } + } if (updateMarkers) handler->updateMarkers(); @@ -130,7 +173,11 @@ bool synchronizeBreakPoints(CIDebugControl* debugControl, if (debugBP > 1) { QList bps; CdbCore::BreakPoint::getBreakPoints(debugControl, &bps, errorMessage); - qDebug().nospace() << "### Breakpoints in engine: " << bps; + 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; } diff --git a/src/plugins/debugger/cdb/cdbbreakpoint.h b/src/plugins/debugger/cdb/cdbbreakpoint.h index ecee377849a..3079ac6476b 100644 --- a/src/plugins/debugger/cdb/cdbbreakpoint.h +++ b/src/plugins/debugger/cdb/cdbbreakpoint.h @@ -42,23 +42,12 @@ QT_BEGIN_NAMESPACE class QDebug; QT_END_NAMESPACE -// Convert breakpoint structs -inline CdbCore::BreakPoint breakPointFromBreakPointData(const Debugger::Internal::BreakpointData &bpd) -{ - CdbCore::BreakPoint rc; - rc.fileName = QDir::toNativeSeparators(bpd.fileName); - rc.condition = bpd.condition; - rc.funcName = bpd.funcName; - rc.ignoreCount = bpd.ignoreCount.isEmpty() ? 0 : bpd.ignoreCount.toInt(); - rc.lineNumber = bpd.lineNumber.isEmpty() ? -1 : bpd.lineNumber.toInt(); - rc.oneShot = false; - rc.enabled = bpd.enabled; - return rc; -} - namespace Debugger { namespace Internal { +// Convert breakpoint structs +CdbCore::BreakPoint breakPointFromBreakPointData(const Debugger::Internal::BreakpointData &bpd); + // Synchronize (halted) engine with BreakHandler. bool synchronizeBreakPoints(CIDebugControl* ctl, CIDebugSymbols *syms, BreakHandler *bh, diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index 02e6491128b..b8c9502d799 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -1539,7 +1539,9 @@ void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP) expression.clear(); } if (!expression.isEmpty()) - m_stoppedMessage = CdbDebugEngine::tr("Breakpoint: %1").arg(expression); + m_stoppedMessage = breakpoint.type == CdbCore::BreakPoint::Code ? + CdbDebugEngine::tr("Breakpoint: %1").arg(expression) : + CdbDebugEngine::tr("Watchpoint: %1").arg(expression); } void CdbDebugEngine::reloadSourceFiles() @@ -1560,7 +1562,8 @@ void CdbDebugEngine::syncDebuggerPaths() unsigned CdbDebugEngine::debuggerCapabilities() const { - return DisassemblerCapability | RegisterCapability | ShowMemoryCapability; + return DisassemblerCapability | RegisterCapability | ShowMemoryCapability + |WatchpointCapability; } // Accessed by DebuggerManager