diff --git a/src/plugins/debugger/cdb/cdbbreakpoint.cpp b/src/plugins/debugger/cdb/cdbbreakpoint.cpp index 035c5d76fca5dc19baaf6f0449cae2dbe518cd1d..345ed2f96807cb23c5464d0ecd0d0a944ebabe88 100644 --- a/src/plugins/debugger/cdb/cdbbreakpoint.cpp +++ b/src/plugins/debugger/cdb/cdbbreakpoint.cpp @@ -28,6 +28,7 @@ **************************************************************************/ #include "cdbbreakpoint.h" +#include "cdbmodules.h" #include "breakhandler.h" #include "cdbdebugengine_p.h" @@ -279,20 +280,48 @@ bool CDBBreakPoint::getBreakPoints(IDebugControl4* debugControl, QList<CDBBreakP return true; } + // Synchronize (halted) engine breakpoints with those of the BreakHandler. bool CDBBreakPoint::synchronizeBreakPoints(IDebugControl4* debugControl, + IDebugSymbols3 *syms, BreakHandler *handler, QString *errorMessage) { typedef QMap<CDBBreakPoint, int> BreakPointIndexMap; - BreakPointIndexMap breakPointIndexMap; - // convert BreakHandler's bps into a map of BreakPoint->BreakHandler->Index if (debugCDB) qDebug() << Q_FUNC_INFO; + BreakPointIndexMap breakPointIndexMap; + // convert BreakHandler's bps into a map of BreakPoint->BreakHandler->Index + // Ignore invalid functions (that could not be found) as they make + // the debugger hang. const int handlerCount = handler->size(); - for (int i=0; i < handlerCount; ++i) - breakPointIndexMap.insert(CDBBreakPoint(*handler->at(i)), i); + const QChar moduleDelimiter = QLatin1Char('!'); + for (int i=0; i < handlerCount; ++i) { + BreakpointData *bd = handler->at(i); + // Function breakpoints: Are the module names specified? + bool breakPointOk = false; + if (bd->funcName.isEmpty()) { + breakPointOk = true; + } else { + switch (resolveSymbol(syms, &bd->funcName, errorMessage)) { + case ResolveSymbolOk: + breakPointOk = true; + break; + case ResolveSymbolAmbiguous: + qWarning("Warning: %s\n", qPrintable(*errorMessage)); + breakPointOk = true; + break; + case ResolveSymbolNotFound: + case ResolveSymbolError: + qWarning("Warning: %s\n", qPrintable(*errorMessage)); + break; + }; + } // function breakpoint + if (breakPointOk) + breakPointIndexMap.insert(CDBBreakPoint(*bd), i); + } + errorMessage->clear(); // get number of engine breakpoints ULONG engineCount; if (!getBreakPointCount(debugControl, &engineCount, errorMessage)) diff --git a/src/plugins/debugger/cdb/cdbbreakpoint.h b/src/plugins/debugger/cdb/cdbbreakpoint.h index 2abdddcb94273e211e4dfe80de4b497eb5ebecd5..3040c0895f030692d0950e80539f59becd7b3531 100644 --- a/src/plugins/debugger/cdb/cdbbreakpoint.h +++ b/src/plugins/debugger/cdb/cdbbreakpoint.h @@ -72,7 +72,8 @@ struct CDBBreakPoint { static bool getBreakPointCount(IDebugControl4* debugControl, ULONG *count, QString *errorMessage = 0); static bool getBreakPoints(IDebugControl4* debugControl, QList<CDBBreakPoint> *bps, QString *errorMessage); // Synchronize (halted) engine with BreakHandler. - static bool synchronizeBreakPoints(IDebugControl4* ctl, BreakHandler *bh, QString *errorMessage); + static bool synchronizeBreakPoints(IDebugControl4* ctl, IDebugSymbols3 *syms, + BreakHandler *bh, QString *errorMessage); // Return a 'canonical' file (using '/' and capitalized drive letter) static QString canonicalSourceFile(const QString &f); diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index 77efd2d4d0b264aac4c493874e44eaabc4958ebd..cf42ce3faf25bbe91a5d79d87256afe6829b5487 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -208,7 +208,7 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent, CdbDebugEn m_currentStackTrace(0), m_firstActivatedFrame(true), m_mode(AttachCore) -{ +{ } bool CdbDebugEnginePrivate::init(QString *errorMessage) @@ -252,7 +252,7 @@ bool CdbDebugEnginePrivate::init(QString *errorMessage) hr = lib.debugCreate( __uuidof(IDebugRegisters2), reinterpret_cast<void**>(&m_pDebugRegisters)); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Creation of IDebugRegisters2 failed: %1").arg(msgDebugEngineComResult(hr)); - return false; + return false; } if (debugCDB) qDebug() << QString::fromLatin1("CDB Initialization succeeded, interrupt time out %1s.").arg(getInterruptTimeOutSecs(m_pDebugControl)); @@ -299,7 +299,7 @@ void CdbDebugEnginePrivate::clearForRun() } void CdbDebugEnginePrivate::cleanStackTrace() -{ +{ if (m_currentStackTrace) { delete m_currentStackTrace; m_currentStackTrace = 0; @@ -366,7 +366,7 @@ void CdbDebugEnginePrivate::clearDisplay() } bool CdbDebugEngine::startDebugger() -{ +{ m_d->clearDisplay(); m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1); QString errorMessage; @@ -489,7 +489,7 @@ void CdbDebugEngine::exitDebugger() // Terminate or detach if we are running HRESULT hr; switch (m_d->m_mode) { - case AttachExternal: + case AttachExternal: wasRunning = m_d->isDebuggeeRunning(); if (wasRunning) { // Process must be stopped in order to detach m_d->interruptInterferiorProcess(&errorMessage); @@ -502,7 +502,7 @@ void CdbDebugEngine::exitDebugger() qDebug() << Q_FUNC_INFO << "detached" << msgDebugEngineComResult(hr); break; case StartExternal: - case StartInternal: + case StartInternal: wasRunning = m_d->isDebuggeeRunning(); if (wasRunning) { // Process must be stopped in order to terminate m_d->interruptInterferiorProcess(&errorMessage); @@ -569,7 +569,7 @@ bool CdbDebugEnginePrivate::updateLocals(int frameIndex, qDebug() << Q_FUNC_INFO << "\n " << frameIndex << formatWatchList(incompletes); m_engine->filterEvaluateWatchers(&incompletes, wh); - if (!incompletes.empty()) { + if (!incompletes.empty()) { const QString msg = QLatin1String("Warning: Locals left in incomplete list: ") + formatWatchList(incompletes); qWarning("%s\n", qPrintable(msg)); } @@ -613,7 +613,7 @@ void CdbDebugEngine::filterEvaluateWatchers(QList<WatchData> *wd, WatchHandler * bool placeHolderSeen = false; for (WatchList::iterator it = wd->begin(); it != wd->end(); ) { if (it->iname.startsWith(watcherPrefix)) { - const bool isPlaceHolder = it->exp.startsWith(lessThan) && it->exp.endsWith(greaterThan); + const bool isPlaceHolder = it->exp.startsWith(lessThan) && it->exp.endsWith(greaterThan); if (isPlaceHolder) { if (!placeHolderSeen) { // Max one place holder it->setChildCount(0); @@ -624,7 +624,7 @@ void CdbDebugEngine::filterEvaluateWatchers(QList<WatchData> *wd, WatchHandler * } else { evaluateWatcher(&(*it)); wh->insertData(*it); - } + } it = wd->erase(it); } else { ++it; @@ -778,7 +778,7 @@ bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessage) // Continue process with notifications bool CdbDebugEnginePrivate::continueInferior(QString *errorMessage) -{ +{ ULONG executionStatus; if (!getExecutionStatus(m_pDebugControl, &executionStatus, errorMessage)) return false; @@ -817,7 +817,7 @@ bool CdbDebugEnginePrivate::interruptInterferiorProcess(QString *errorMessage) *errorMessage = QString::fromLatin1("DebugBreakProcess failed: %1").arg(Core::Utils::winErrorMessage(GetLastError())); return false; } -#if 0 +#if 0 const HRESULT hr = m_pDebugControl->SetInterrupt(DEBUG_INTERRUPT_ACTIVE|DEBUG_INTERRUPT_EXIT); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Unable to interrupt debuggee after %1s: %2"). @@ -864,7 +864,7 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v if (debugCDB) qDebug() << Q_FUNC_INFO << expr << value; const int frameIndex = m_d->m_debuggerManagerAccess->stackHandler()->currentIndex(); - QString errorMessage; + QString errorMessage; bool success = false; do { QString newValue; @@ -872,7 +872,7 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v if (!sg) break; if (!sg->assignValue(expr, value, &newValue, &errorMessage)) - break; + break; // Update view WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler(); if (WatchData *fwd = watchHandler->findData(expr)) { @@ -1032,6 +1032,7 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa } return CDBBreakPoint::synchronizeBreakPoints(m_pDebugControl, + m_pDebugSymbols, m_debuggerManagerAccess->breakHandler(), errorMessage); } @@ -1045,7 +1046,7 @@ void CdbDebugEngine::saveSessionData() } void CdbDebugEngine::reloadDisassembler() -{ +{ enum { ContextLines = 40 }; // Do we have a top stack frame? const ULONG64 offset = m_d->m_currentStackTrace ? m_d->m_currentStackTrace->instructionOffset() : ULONG64(0); @@ -1140,7 +1141,7 @@ void CdbDebugEngine::timerEvent(QTimerEvent* te) break; case E_UNEXPECTED: // Occurs on ExitProcess. killWatchTimer(); - break; + break; } } @@ -1260,6 +1261,10 @@ void CdbDebugEnginePrivate::updateStackTrace() qWarning("%s: failed to create trace context: %s", Q_FUNC_INFO, qPrintable(errorMessage)); return; } + // Disassembling slows things down a bit. Assembler is still available via menu. +#if 0 + m_engine->reloadDisassembler(); // requires stack trace +#endif const QList<StackFrame> stackFrames = m_currentStackTrace->frames(); // find the first usable frame and select it int current = -1; @@ -1275,7 +1280,7 @@ void CdbDebugEnginePrivate::updateStackTrace() if (current >= 0) { m_debuggerManagerAccess->stackHandler()->setCurrentIndex(current); m_engine->activateFrame(current); - } + } } diff --git a/src/plugins/debugger/cdb/cdbmodules.cpp b/src/plugins/debugger/cdb/cdbmodules.cpp index 6e97800e8437f0cbde01b3c30f580a7fdd6dcd72..70e8bd7d52379716dd8c98965a6d490702cd9dbf 100644 --- a/src/plugins/debugger/cdb/cdbmodules.cpp +++ b/src/plugins/debugger/cdb/cdbmodules.cpp @@ -77,5 +77,61 @@ bool getModuleList(IDebugSymbols3 *syms, QList<Module> *modules, QString *errorM return true; } +// Search symbols matching a pattern +bool searchSymbols(IDebugSymbols3 *syms, const QString &pattern, + QStringList *matches, QString *errorMessage) +{ + matches->clear(); + ULONG64 handle; + // E_NOINTERFACE means "no match" + HRESULT hr = syms->StartSymbolMatchWide(pattern.utf16(), &handle); + if (hr == E_NOINTERFACE) { + syms->EndSymbolMatch(handle); + return true; + } + if (FAILED(hr)) { + *errorMessage= msgComFailed("StartSymbolMatchWide", hr); + return false; + } + WCHAR wszBuf[MAX_PATH]; + while (true) { + hr = syms->GetNextSymbolMatchWide(handle, wszBuf, MAX_PATH - 1, 0, 0); + if (hr == E_NOINTERFACE) + break; + if (hr == S_OK) + matches->push_back(QString::fromUtf16(wszBuf)); + } + syms->EndSymbolMatch(handle); + if (matches->empty()) + *errorMessage = QString::fromLatin1("No symbol matches '%1'.").arg(pattern); + return true; +} + +// Add missing the module specifier: "main" -> "project!main" + +ResolveSymbolResult resolveSymbol(IDebugSymbols3 *syms, QString *symbol, + QString *errorMessage) +{ + // Is it an incomplete symbol? + if (symbol->contains(QLatin1Char('!'))) + return ResolveSymbolOk; + // 'main' is a #define for gdb, but not for VS + if (*symbol == QLatin1String("qMain")) + *symbol = QLatin1String("main"); + // resolve + QStringList matches; + if (!searchSymbols(syms, *symbol, &matches, errorMessage)) + return ResolveSymbolError; + if (matches.empty()) + return ResolveSymbolNotFound; + *symbol = matches.front(); + if (matches.size() > 1) { + *errorMessage = QString::fromLatin1("Ambiguous symbol '%1': %2"). + arg(*symbol, matches.join(QString(QLatin1Char(' ')))); + return ResolveSymbolAmbiguous; + } + return ResolveSymbolOk; +} + } } diff --git a/src/plugins/debugger/cdb/cdbmodules.h b/src/plugins/debugger/cdb/cdbmodules.h index 3a959b7f5751a5c9b355053775cef7d8219647b5..a7be7a943215a24887f49b22a97640a67e55a827 100644 --- a/src/plugins/debugger/cdb/cdbmodules.h +++ b/src/plugins/debugger/cdb/cdbmodules.h @@ -42,6 +42,17 @@ namespace Internal { class Module; bool getModuleList(IDebugSymbols3 *syms, QList<Module> *modules, QString *errorMessage); +// Search symbols matching a pattern +bool searchSymbols(IDebugSymbols3 *syms, const QString &pattern, + QStringList *matches, QString *errorMessage); + +// ResolveSymbol: For symbols that are missing the module specifier, +// find the module and expand: "main" -> "project!main". + +enum ResolveSymbolResult { ResolveSymbolOk, ResolveSymbolAmbiguous, + ResolveSymbolNotFound, ResolveSymbolError }; + +ResolveSymbolResult resolveSymbol(IDebugSymbols3 *syms, QString *symbol, QString *errorMessage); } }