Make break by function work for CDB.

Add function to automagically resolve function symbol names
that are missing the module name (module!foo). Kill some
trailing whitespace. Add a hack to transform qMain->main for VS.
This commit is contained in:
Friedemann Kleint
2009-04-15 10:05:40 +02:00
parent 0c03179fac
commit d11242feba
5 changed files with 123 additions and 21 deletions

View File

@@ -28,6 +28,7 @@
**************************************************************************/ **************************************************************************/
#include "cdbbreakpoint.h" #include "cdbbreakpoint.h"
#include "cdbmodules.h"
#include "breakhandler.h" #include "breakhandler.h"
#include "cdbdebugengine_p.h" #include "cdbdebugengine_p.h"
@@ -279,20 +280,48 @@ bool CDBBreakPoint::getBreakPoints(IDebugControl4* debugControl, QList<CDBBreakP
return true; return true;
} }
// Synchronize (halted) engine breakpoints with those of the BreakHandler. // Synchronize (halted) engine breakpoints with those of the BreakHandler.
bool CDBBreakPoint::synchronizeBreakPoints(IDebugControl4* debugControl, bool CDBBreakPoint::synchronizeBreakPoints(IDebugControl4* debugControl,
IDebugSymbols3 *syms,
BreakHandler *handler, BreakHandler *handler,
QString *errorMessage) QString *errorMessage)
{ {
typedef QMap<CDBBreakPoint, int> BreakPointIndexMap; typedef QMap<CDBBreakPoint, int> BreakPointIndexMap;
BreakPointIndexMap breakPointIndexMap;
// convert BreakHandler's bps into a map of BreakPoint->BreakHandler->Index
if (debugCDB) if (debugCDB)
qDebug() << Q_FUNC_INFO; 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(); const int handlerCount = handler->size();
for (int i=0; i < handlerCount; ++i) const QChar moduleDelimiter = QLatin1Char('!');
breakPointIndexMap.insert(CDBBreakPoint(*handler->at(i)), i); 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 // get number of engine breakpoints
ULONG engineCount; ULONG engineCount;
if (!getBreakPointCount(debugControl, &engineCount, errorMessage)) if (!getBreakPointCount(debugControl, &engineCount, errorMessage))

View File

@@ -72,7 +72,8 @@ struct CDBBreakPoint {
static bool getBreakPointCount(IDebugControl4* debugControl, ULONG *count, QString *errorMessage = 0); static bool getBreakPointCount(IDebugControl4* debugControl, ULONG *count, QString *errorMessage = 0);
static bool getBreakPoints(IDebugControl4* debugControl, QList<CDBBreakPoint> *bps, QString *errorMessage); static bool getBreakPoints(IDebugControl4* debugControl, QList<CDBBreakPoint> *bps, QString *errorMessage);
// Synchronize (halted) engine with BreakHandler. // 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) // Return a 'canonical' file (using '/' and capitalized drive letter)
static QString canonicalSourceFile(const QString &f); static QString canonicalSourceFile(const QString &f);

View File

@@ -208,7 +208,7 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent, CdbDebugEn
m_currentStackTrace(0), m_currentStackTrace(0),
m_firstActivatedFrame(true), m_firstActivatedFrame(true),
m_mode(AttachCore) m_mode(AttachCore)
{ {
} }
bool CdbDebugEnginePrivate::init(QString *errorMessage) bool CdbDebugEnginePrivate::init(QString *errorMessage)
@@ -252,7 +252,7 @@ bool CdbDebugEnginePrivate::init(QString *errorMessage)
hr = lib.debugCreate( __uuidof(IDebugRegisters2), reinterpret_cast<void**>(&m_pDebugRegisters)); hr = lib.debugCreate( __uuidof(IDebugRegisters2), reinterpret_cast<void**>(&m_pDebugRegisters));
if (FAILED(hr)) { if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Creation of IDebugRegisters2 failed: %1").arg(msgDebugEngineComResult(hr)); *errorMessage = QString::fromLatin1("Creation of IDebugRegisters2 failed: %1").arg(msgDebugEngineComResult(hr));
return false; return false;
} }
if (debugCDB) if (debugCDB)
qDebug() << QString::fromLatin1("CDB Initialization succeeded, interrupt time out %1s.").arg(getInterruptTimeOutSecs(m_pDebugControl)); qDebug() << QString::fromLatin1("CDB Initialization succeeded, interrupt time out %1s.").arg(getInterruptTimeOutSecs(m_pDebugControl));
@@ -299,7 +299,7 @@ void CdbDebugEnginePrivate::clearForRun()
} }
void CdbDebugEnginePrivate::cleanStackTrace() void CdbDebugEnginePrivate::cleanStackTrace()
{ {
if (m_currentStackTrace) { if (m_currentStackTrace) {
delete m_currentStackTrace; delete m_currentStackTrace;
m_currentStackTrace = 0; m_currentStackTrace = 0;
@@ -366,7 +366,7 @@ void CdbDebugEnginePrivate::clearDisplay()
} }
bool CdbDebugEngine::startDebugger() bool CdbDebugEngine::startDebugger()
{ {
m_d->clearDisplay(); m_d->clearDisplay();
m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1); m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1);
QString errorMessage; QString errorMessage;
@@ -489,7 +489,7 @@ void CdbDebugEngine::exitDebugger()
// Terminate or detach if we are running // Terminate or detach if we are running
HRESULT hr; HRESULT hr;
switch (m_d->m_mode) { switch (m_d->m_mode) {
case AttachExternal: case AttachExternal:
wasRunning = m_d->isDebuggeeRunning(); wasRunning = m_d->isDebuggeeRunning();
if (wasRunning) { // Process must be stopped in order to detach if (wasRunning) { // Process must be stopped in order to detach
m_d->interruptInterferiorProcess(&errorMessage); m_d->interruptInterferiorProcess(&errorMessage);
@@ -502,7 +502,7 @@ void CdbDebugEngine::exitDebugger()
qDebug() << Q_FUNC_INFO << "detached" << msgDebugEngineComResult(hr); qDebug() << Q_FUNC_INFO << "detached" << msgDebugEngineComResult(hr);
break; break;
case StartExternal: case StartExternal:
case StartInternal: case StartInternal:
wasRunning = m_d->isDebuggeeRunning(); wasRunning = m_d->isDebuggeeRunning();
if (wasRunning) { // Process must be stopped in order to terminate if (wasRunning) { // Process must be stopped in order to terminate
m_d->interruptInterferiorProcess(&errorMessage); m_d->interruptInterferiorProcess(&errorMessage);
@@ -569,7 +569,7 @@ bool CdbDebugEnginePrivate::updateLocals(int frameIndex,
qDebug() << Q_FUNC_INFO << "\n " << frameIndex << formatWatchList(incompletes); qDebug() << Q_FUNC_INFO << "\n " << frameIndex << formatWatchList(incompletes);
m_engine->filterEvaluateWatchers(&incompletes, wh); m_engine->filterEvaluateWatchers(&incompletes, wh);
if (!incompletes.empty()) { if (!incompletes.empty()) {
const QString msg = QLatin1String("Warning: Locals left in incomplete list: ") + formatWatchList(incompletes); const QString msg = QLatin1String("Warning: Locals left in incomplete list: ") + formatWatchList(incompletes);
qWarning("%s\n", qPrintable(msg)); qWarning("%s\n", qPrintable(msg));
} }
@@ -613,7 +613,7 @@ void CdbDebugEngine::filterEvaluateWatchers(QList<WatchData> *wd, WatchHandler *
bool placeHolderSeen = false; bool placeHolderSeen = false;
for (WatchList::iterator it = wd->begin(); it != wd->end(); ) { for (WatchList::iterator it = wd->begin(); it != wd->end(); ) {
if (it->iname.startsWith(watcherPrefix)) { 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 (isPlaceHolder) {
if (!placeHolderSeen) { // Max one place holder if (!placeHolderSeen) { // Max one place holder
it->setChildCount(0); it->setChildCount(0);
@@ -624,7 +624,7 @@ void CdbDebugEngine::filterEvaluateWatchers(QList<WatchData> *wd, WatchHandler *
} else { } else {
evaluateWatcher(&(*it)); evaluateWatcher(&(*it));
wh->insertData(*it); wh->insertData(*it);
} }
it = wd->erase(it); it = wd->erase(it);
} else { } else {
++it; ++it;
@@ -778,7 +778,7 @@ bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessage)
// Continue process with notifications // Continue process with notifications
bool CdbDebugEnginePrivate::continueInferior(QString *errorMessage) bool CdbDebugEnginePrivate::continueInferior(QString *errorMessage)
{ {
ULONG executionStatus; ULONG executionStatus;
if (!getExecutionStatus(m_pDebugControl, &executionStatus, errorMessage)) if (!getExecutionStatus(m_pDebugControl, &executionStatus, errorMessage))
return false; return false;
@@ -817,7 +817,7 @@ bool CdbDebugEnginePrivate::interruptInterferiorProcess(QString *errorMessage)
*errorMessage = QString::fromLatin1("DebugBreakProcess failed: %1").arg(Core::Utils::winErrorMessage(GetLastError())); *errorMessage = QString::fromLatin1("DebugBreakProcess failed: %1").arg(Core::Utils::winErrorMessage(GetLastError()));
return false; return false;
} }
#if 0 #if 0
const HRESULT hr = m_pDebugControl->SetInterrupt(DEBUG_INTERRUPT_ACTIVE|DEBUG_INTERRUPT_EXIT); const HRESULT hr = m_pDebugControl->SetInterrupt(DEBUG_INTERRUPT_ACTIVE|DEBUG_INTERRUPT_EXIT);
if (FAILED(hr)) { if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Unable to interrupt debuggee after %1s: %2"). *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) if (debugCDB)
qDebug() << Q_FUNC_INFO << expr << value; qDebug() << Q_FUNC_INFO << expr << value;
const int frameIndex = m_d->m_debuggerManagerAccess->stackHandler()->currentIndex(); const int frameIndex = m_d->m_debuggerManagerAccess->stackHandler()->currentIndex();
QString errorMessage; QString errorMessage;
bool success = false; bool success = false;
do { do {
QString newValue; QString newValue;
@@ -872,7 +872,7 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v
if (!sg) if (!sg)
break; break;
if (!sg->assignValue(expr, value, &newValue, &errorMessage)) if (!sg->assignValue(expr, value, &newValue, &errorMessage))
break; break;
// Update view // Update view
WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler(); WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler();
if (WatchData *fwd = watchHandler->findData(expr)) { if (WatchData *fwd = watchHandler->findData(expr)) {
@@ -1032,6 +1032,7 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa
} }
return CDBBreakPoint::synchronizeBreakPoints(m_pDebugControl, return CDBBreakPoint::synchronizeBreakPoints(m_pDebugControl,
m_pDebugSymbols,
m_debuggerManagerAccess->breakHandler(), m_debuggerManagerAccess->breakHandler(),
errorMessage); errorMessage);
} }
@@ -1045,7 +1046,7 @@ void CdbDebugEngine::saveSessionData()
} }
void CdbDebugEngine::reloadDisassembler() void CdbDebugEngine::reloadDisassembler()
{ {
enum { ContextLines = 40 }; enum { ContextLines = 40 };
// Do we have a top stack frame? // Do we have a top stack frame?
const ULONG64 offset = m_d->m_currentStackTrace ? m_d->m_currentStackTrace->instructionOffset() : ULONG64(0); const ULONG64 offset = m_d->m_currentStackTrace ? m_d->m_currentStackTrace->instructionOffset() : ULONG64(0);
@@ -1140,7 +1141,7 @@ void CdbDebugEngine::timerEvent(QTimerEvent* te)
break; break;
case E_UNEXPECTED: // Occurs on ExitProcess. case E_UNEXPECTED: // Occurs on ExitProcess.
killWatchTimer(); killWatchTimer();
break; break;
} }
} }
@@ -1260,6 +1261,10 @@ void CdbDebugEnginePrivate::updateStackTrace()
qWarning("%s: failed to create trace context: %s", Q_FUNC_INFO, qPrintable(errorMessage)); qWarning("%s: failed to create trace context: %s", Q_FUNC_INFO, qPrintable(errorMessage));
return; 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(); const QList<StackFrame> stackFrames = m_currentStackTrace->frames();
// find the first usable frame and select it // find the first usable frame and select it
int current = -1; int current = -1;
@@ -1275,7 +1280,7 @@ void CdbDebugEnginePrivate::updateStackTrace()
if (current >= 0) { if (current >= 0) {
m_debuggerManagerAccess->stackHandler()->setCurrentIndex(current); m_debuggerManagerAccess->stackHandler()->setCurrentIndex(current);
m_engine->activateFrame(current); m_engine->activateFrame(current);
} }
} }

View File

@@ -77,5 +77,61 @@ bool getModuleList(IDebugSymbols3 *syms, QList<Module> *modules, QString *errorM
return true; 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;
}
} }
} }

View File

@@ -42,6 +42,17 @@ namespace Internal {
class Module; class Module;
bool getModuleList(IDebugSymbols3 *syms, QList<Module> *modules, QString *errorMessage); 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);
} }
} }