Make CDB load custom dumpers.

Load in a 'well-defined' (temporary) breakpoint
at main().
This commit is contained in:
Friedemann Kleint
2009-04-22 17:28:26 +02:00
parent ef8e69d96a
commit 52915776cd
14 changed files with 594 additions and 157 deletions

View File

@@ -2529,7 +2529,11 @@ void *qDumpObjectData440(
int protocolVersion, int protocolVersion,
int token, int token,
void *data, void *data,
#ifdef Q_CC_MSVC // CDB cannot handle boolean parameters
int dumpChildren,
#else
bool dumpChildren, bool dumpChildren,
#endif
int extraInt0, int extraInt0,
int extraInt1, int extraInt1,
int extraInt2, int extraInt2,

View File

@@ -48,7 +48,8 @@ static const char sourceFileQuoteC = '`';
CDBBreakPoint::CDBBreakPoint() : CDBBreakPoint::CDBBreakPoint() :
ignoreCount(0), ignoreCount(0),
lineNumber(-1) lineNumber(-1),
oneShot(false)
{ {
} }
@@ -57,7 +58,8 @@ CDBBreakPoint::CDBBreakPoint(const BreakpointData &bpd) :
condition(bpd.condition), condition(bpd.condition),
ignoreCount(0), ignoreCount(0),
funcName(bpd.funcName), funcName(bpd.funcName),
lineNumber(-1) lineNumber(-1),
oneShot(false)
{ {
if (!bpd.ignoreCount.isEmpty()) if (!bpd.ignoreCount.isEmpty())
ignoreCount = bpd.ignoreCount.toInt(); ignoreCount = bpd.ignoreCount.toInt();
@@ -75,6 +77,10 @@ int CDBBreakPoint::compare(const CDBBreakPoint& rhs) const
return 1; return 1;
if (lineNumber < rhs.lineNumber) if (lineNumber < rhs.lineNumber)
return -1; return -1;
if (oneShot && !rhs.oneShot)
return 1;
if (!oneShot && rhs.oneShot)
return -1;
if (const int fileCmp = fileName.compare(rhs.fileName)) if (const int fileCmp = fileName.compare(rhs.fileName))
return fileCmp; return fileCmp;
if (const int funcCmp = funcName.compare(rhs.funcName)) if (const int funcCmp = funcName.compare(rhs.funcName))
@@ -87,7 +93,8 @@ int CDBBreakPoint::compare(const CDBBreakPoint& rhs) const
void CDBBreakPoint::clear() void CDBBreakPoint::clear()
{ {
ignoreCount = 0; ignoreCount = 0;
clearExpressionData(); oneShot = false;
clearExpressionData();
} }
void CDBBreakPoint::clearExpressionData() void CDBBreakPoint::clearExpressionData()
@@ -110,6 +117,8 @@ QDebug operator<<(QDebug dbg, const CDBBreakPoint &bp)
nsp << " condition='" << bp.condition << '\''; nsp << " condition='" << bp.condition << '\'';
if (bp.ignoreCount) if (bp.ignoreCount)
nsp << " ignoreCount=" << bp.ignoreCount; nsp << " ignoreCount=" << bp.ignoreCount;
if (bp.oneShot)
nsp << " oneShot";
return dbg; return dbg;
} }
@@ -144,7 +153,10 @@ bool CDBBreakPoint::apply(CIDebugBreakpoint *ibp, QString *errorMessage) const
} }
// Pass Count is ignoreCount + 1 // Pass Count is ignoreCount + 1
ibp->SetPassCount(ignoreCount + 1u); ibp->SetPassCount(ignoreCount + 1u);
ibp->AddFlags(DEBUG_BREAKPOINT_ENABLED); ULONG flags = DEBUG_BREAKPOINT_ENABLED;
if (oneShot)
flags |= DEBUG_BREAKPOINT_ONE_SHOT;
ibp->AddFlags(flags);
return true; return true;
} }
@@ -190,6 +202,10 @@ bool CDBBreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage)
ibp->GetPassCount(&ignoreCount); ibp->GetPassCount(&ignoreCount);
if (ignoreCount) if (ignoreCount)
ignoreCount--; ignoreCount--;
ULONG flags = 0;
ibp->GetFlags(&flags);
if (flags & DEBUG_BREAKPOINT_ONE_SHOT)
oneShot = true;
const QString expr = QString::fromUtf16(wszBuf); const QString expr = QString::fromUtf16(wszBuf);
if (!parseExpression(expr)) { if (!parseExpression(expr)) {
*errorMessage = QString::fromLatin1("Parsing of '%1' failed.").arg(expr); *errorMessage = QString::fromLatin1("Parsing of '%1' failed.").arg(expr);

View File

@@ -83,6 +83,7 @@ struct CDBBreakPoint
unsigned long ignoreCount; // ignore count associated with breakpoint unsigned long ignoreCount; // ignore count associated with breakpoint
int lineNumber; // line in source file int lineNumber; // line in source file
QString funcName; // name of containing function QString funcName; // name of containing function
bool oneShot;
}; };
QDebug operator<<(QDebug, const CDBBreakPoint &bp); QDebug operator<<(QDebug, const CDBBreakPoint &bp);

View File

@@ -53,6 +53,7 @@
#include <utils/consoleprocess.h> #include <utils/consoleprocess.h>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtCore/QTimer>
#include <QtCore/QTimerEvent> #include <QtCore/QTimerEvent>
#include <QtCore/QFileInfo> #include <QtCore/QFileInfo>
#include <QtCore/QDir> #include <QtCore/QDir>
@@ -96,13 +97,13 @@ QString msgDebugEngineComResult(HRESULT hr)
case E_UNEXPECTED: case E_UNEXPECTED:
return QLatin1String("E_UNEXPECTED"); return QLatin1String("E_UNEXPECTED");
case E_NOTIMPL: case E_NOTIMPL:
return QLatin1String("E_NOTIMPL"); return QLatin1String("E_NOTIMPL");
} }
if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED))
return QLatin1String("ERROR_ACCESS_DENIED");; return QLatin1String("ERROR_ACCESS_DENIED");;
if (hr == HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT)) if (hr == HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT))
return QLatin1String("STATUS_CONTROL_C_EXIT"); return QLatin1String("STATUS_CONTROL_C_EXIT");
return Core::Utils::winErrorMessage(HRESULT_CODE(hr)); return QLatin1String("E_FAIL ") + Core::Utils::winErrorMessage(HRESULT_CODE(hr));
} }
static QString msgStackIndexOutOfRange(int idx, int size) static QString msgStackIndexOutOfRange(int idx, int size)
@@ -450,14 +451,16 @@ void CdbDebugEnginePrivate::clearDisplay()
bool CdbDebugEngine::startDebugger() bool CdbDebugEngine::startDebugger()
{ {
m_d->clearDisplay(); m_d->clearDisplay();
const DebuggerStartMode mode = m_d->m_debuggerManager->startMode();
// Figure out dumper. @TODO: same in gdb... // Figure out dumper. @TODO: same in gdb...
bool dumperEnabled = false && m_d->m_debuggerManager->qtDumperLibraryEnabled(); const QString dumperLibName = QDir::toNativeSeparators(m_d->m_debuggerManagerAccess->qtDumperLibraryName());
const QString dumperLibName = QDir::toNativeSeparators(m_d->m_debuggerManager->qtDumperLibraryName()); bool dumperEnabled = mode != AttachCore && !dumperLibName.isEmpty()
&& m_d->m_debuggerManagerAccess->qtDumperLibraryEnabled();
if (dumperEnabled) { if (dumperEnabled) {
const QFileInfo fi(dumperLibName); const QFileInfo fi(dumperLibName);
if (!fi.isFile()) { if (!fi.isFile()) {
const QString msg = tr("The dumper library '%1' does not exist.").arg(dumperLibName); const QString msg = tr("The dumper library '%1' does not exist.").arg(dumperLibName);
m_d->m_debuggerManager->showQtDumperLibraryWarning(msg); m_d->m_debuggerManagerAccess->showQtDumperLibraryWarning(msg);
dumperEnabled = false; dumperEnabled = false;
} }
} }
@@ -466,7 +469,6 @@ bool CdbDebugEngine::startDebugger()
QString errorMessage; QString errorMessage;
bool rc = false; bool rc = false;
m_d->clearForRun(); m_d->clearForRun();
const DebuggerStartMode mode = m_d->m_debuggerManager->startMode();
switch (mode) { switch (mode) {
case AttachExternal: case AttachExternal:
rc = startAttachDebugger(m_d->m_debuggerManager->m_attachedPID, &errorMessage); rc = startAttachDebugger(m_d->m_debuggerManager->m_attachedPID, &errorMessage);
@@ -561,6 +563,52 @@ bool CdbDebugEngine::startDebuggerWithExecutable(DebuggerStartMode sm, QString *
return true; return true;
} }
// check for a breakpoint at 'main()'
static inline bool hasBreakPointAtMain(const BreakHandler *bp)
{
if (const int count = bp->size()) {
// check all variations, resolved or not
const QString main = QLatin1String("main");
const QString qMain = QLatin1String("qMain");
const QString moduleMainPattern = QLatin1String("!main");
for (int i = 0; i < count ; i++) {
const QString &function = bp->at(i)->funcName;
if (function == main || function == qMain || function.endsWith(moduleMainPattern))
return true;
}
}
return false;
}
void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle)
{
setDebuggeeHandles(reinterpret_cast<HANDLE>(processHandle), reinterpret_cast<HANDLE>(initialThreadHandle));
m_debuggerManagerAccess->notifyInferiorRunning();
ULONG currentThreadId;
if (SUCCEEDED(m_cif.debugSystemObjects->GetThreadIdByHandle(initialThreadHandle, &currentThreadId))) {
m_currentThreadId = currentThreadId;
} else {
m_currentThreadId = 0;
}
// Set initial breakpoints
if (m_debuggerManagerAccess->breakHandler()->hasPendingBreakpoints())
m_engine->attemptBreakpointSynchronization();
// At any event, we want a temporary breakpoint at main() to load
// the dumpers.
if (m_dumper.state() == CdbDumperHelper::NotLoaded) {
if (!hasBreakPointAtMain(m_debuggerManagerAccess->breakHandler())) {
CDBBreakPoint mainBP;
// Do not resolve at this point in the rare event someone
// has main in a module
mainBP.funcName = QLatin1String("main");
mainBP.oneShot = true;
QString errorMessage;
if (!mainBP.add(m_cif.debugControl, &errorMessage))
m_debuggerManagerAccess->showQtDumperLibraryWarning(errorMessage);
}
}
}
void CdbDebugEngine::processTerminated(unsigned long exitCode) void CdbDebugEngine::processTerminated(unsigned long exitCode)
{ {
if (debugCDB) if (debugCDB)
@@ -859,13 +907,18 @@ void CdbDebugEngine::continueInferior()
} }
// Continue process without notifications // Continue process without notifications
bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessage) bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessagePtr /* = 0 */)
{ {
if (debugCDB) if (debugCDB)
qDebug() << Q_FUNC_INFO; qDebug() << Q_FUNC_INFO;
const HRESULT hr = m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_GO); const HRESULT hr = m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_GO);
if (FAILED(hr)) { if (FAILED(hr)) {
*errorMessage = msgComFailed("SetExecutionStatus", hr); const QString errorMessage = msgComFailed("SetExecutionStatus", hr);
if (errorMessagePtr) {
*errorMessagePtr = errorMessage;
} else {
qWarning("continueInferiorProcess: %s\n", qPrintable(errorMessage));
}
return false; return false;
} }
return true; return true;
@@ -992,10 +1045,10 @@ void CdbDebugEngine::executeDebuggerCommand(const QString &command)
bool CdbDebugEnginePrivate::executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage) bool CdbDebugEnginePrivate::executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage)
{ {
if (debugCDB)
qDebug() << Q_FUNC_INFO << command;
// output to all clients, else we do not see anything // output to all clients, else we do not see anything
const HRESULT hr = ctrl->ExecuteWide(DEBUG_OUTCTL_ALL_CLIENTS, command.utf16(), 0); const HRESULT hr = ctrl->ExecuteWide(DEBUG_OUTCTL_ALL_CLIENTS, command.utf16(), 0);
if (debugCDB)
qDebug() << "executeDebuggerCommand" << command << SUCCEEDED(hr);
if (FAILED(hr)) { if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Unable to execute '%1': %2"). *errorMessage = QString::fromLatin1("Unable to execute '%1': %2").
arg(command, msgDebugEngineComResult(hr)); arg(command, msgDebugEngineComResult(hr));
@@ -1300,10 +1353,19 @@ void CdbDebugEnginePrivate::handleDebugEvent()
switch (mode) { switch (mode) {
case BreakEventHandle: case BreakEventHandle:
case BreakEventMain:
if (mode == BreakEventMain)
m_dumper.load(m_debuggerManager, m_debuggerManagerAccess);
m_debuggerManagerAccess->notifyInferiorStopped(); m_debuggerManagerAccess->notifyInferiorStopped();
updateThreadList(); updateThreadList();
updateStackTrace(); updateStackTrace();
break; break;
case BreakEventMainLoadDumpers:
// Temp stop to load dumpers
m_dumper.load(m_debuggerManager, m_debuggerManagerAccess);
m_engine->startWatchTimer();
continueInferiorProcess();
break;
case BreakEventIgnoreOnce: case BreakEventIgnoreOnce:
m_engine->startWatchTimer(); m_engine->startWatchTimer();
break; break;
@@ -1417,17 +1479,6 @@ void CdbDebugEnginePrivate::handleModuleLoad(const QString &name)
if (debugCDB>2) if (debugCDB>2)
qDebug() << Q_FUNC_INFO << "\n " << name; qDebug() << Q_FUNC_INFO << "\n " << name;
updateModules(); updateModules();
// Call the dumper helper hook and notify about progress.
bool ignoreNextBreakPoint;
if (m_dumper.moduleLoadHook(name, &ignoreNextBreakPoint)) {
if (m_dumper.state() == CdbDumperHelper::Loaded)
m_debuggerManagerAccess->showDebuggerOutput(QLatin1String(dumperPrefixC), QString::fromLatin1("Dumpers loaded: %1").arg(m_dumper.library()));
} else {
m_debuggerManager->showQtDumperLibraryWarning(m_dumper.errorMessage());
m_debuggerManagerAccess->showDebuggerOutput(QLatin1String(dumperPrefixC), QString::fromLatin1("Unable to load dumpers: %1").arg(m_dumper.errorMessage()));
}
if (ignoreNextBreakPoint)
m_breakEventMode = BreakEventIgnoreOnce;
} }
void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP) void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP)
@@ -1435,6 +1486,17 @@ void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP)
Q_UNUSED(pBP) Q_UNUSED(pBP)
if (debugCDB) if (debugCDB)
qDebug() << Q_FUNC_INFO; qDebug() << Q_FUNC_INFO;
// Did we hit main() and did the user want that or is that just
// our internal BP to load the dumpers?
QString errorMessage;
CDBBreakPoint bp;
if (bp.retrieve(pBP, &errorMessage) && !bp.funcName.isEmpty()) {
if (bp.funcName == QLatin1String("main") || bp.funcName.endsWith(QLatin1String("!main"))) {
m_breakEventMode = bp.oneShot ? BreakEventMainLoadDumpers : BreakEventMain;
}
if (debugCDB)
qDebug() << bp << " b-mode=" << m_breakEventMode;
}
} }
void CdbDebugEngine::reloadSourceFiles() void CdbDebugEngine::reloadSourceFiles()

View File

@@ -98,6 +98,10 @@ struct CdbDebugEnginePrivate
enum HandleBreakEventMode { // Special modes for break event handler. enum HandleBreakEventMode { // Special modes for break event handler.
BreakEventHandle, BreakEventHandle,
BreakEventIgnoreOnce, BreakEventIgnoreOnce,
// We hit main (and the user intended it)
BreakEventMain,
// We hit main (and the user did not intend it, just load dumpers)
BreakEventMainLoadDumpers,
BreakEventSyncBreakPoints, BreakEventSyncBreakPoints,
}; };
@@ -107,6 +111,7 @@ struct CdbDebugEnginePrivate
bool init(QString *errorMessage); bool init(QString *errorMessage);
~CdbDebugEnginePrivate(); ~CdbDebugEnginePrivate();
void processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle);
void setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread); void setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread);
bool isDebuggeeRunning() const { return m_watchTimer != -1; } bool isDebuggeeRunning() const { return m_watchTimer != -1; }
@@ -125,7 +130,7 @@ struct CdbDebugEnginePrivate
bool interruptInterferiorProcess(QString *errorMessage); bool interruptInterferiorProcess(QString *errorMessage);
bool continueInferiorProcess(QString *errorMessage); bool continueInferiorProcess(QString *errorMessage = 0);
bool continueInferior(QString *errorMessage); bool continueInferior(QString *errorMessage);
bool attemptBreakpointSynchronization(QString *errorMessage); bool attemptBreakpointSynchronization(QString *errorMessage);

View File

@@ -247,7 +247,8 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str)
break; break;
case EXCEPTION_ACCESS_VIOLATION: { case EXCEPTION_ACCESS_VIOLATION: {
const bool writeOperation = e->ExceptionInformation[0]; const bool writeOperation = e->ExceptionInformation[0];
str << (writeOperation ? "write access violation" : "read access violation"); str << (writeOperation ? "write" : "read")
<< " access violation at: 0x" << e->ExceptionInformation[1];
} }
break; break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
@@ -342,8 +343,10 @@ STDMETHODIMP CdbDebugEventCallback::Exception(
QString msg; QString msg;
{ {
QTextStream str(&msg); QTextStream str(&msg);
formatException(Exception, m_pEngine->m_d->m_cif, str); formatException(Exception, m_pEngine->m_d->m_cif, str);
} }
if (debugCDB)
qDebug() << Q_FUNC_INFO << '\n' << msg;
m_pEngine->m_d->m_debuggerManagerAccess->showApplicationOutput(msg); m_pEngine->m_d->m_debuggerManagerAccess->showApplicationOutput(msg);
return S_OK; return S_OK;
} }
@@ -402,18 +405,7 @@ STDMETHODIMP CdbDebugEventCallback::CreateProcess(
Q_UNUSED(StartOffset) Q_UNUSED(StartOffset)
if (debugCDB) if (debugCDB)
qDebug() << Q_FUNC_INFO << ModuleName; qDebug() << Q_FUNC_INFO << ModuleName;
m_pEngine->m_d->processCreatedAttached(Handle, InitialThreadHandle);
m_pEngine->m_d->setDebuggeeHandles(reinterpret_cast<HANDLE>(Handle), reinterpret_cast<HANDLE>(InitialThreadHandle));
m_pEngine->m_d->m_debuggerManagerAccess->notifyInferiorRunning();
ULONG currentThreadId;
if (SUCCEEDED(m_pEngine->m_d->m_cif.debugSystemObjects->GetThreadIdByHandle(InitialThreadHandle, &currentThreadId)))
m_pEngine->m_d->m_currentThreadId = currentThreadId;
else
m_pEngine->m_d->m_currentThreadId = 0;
// Set initial breakpoints
if (m_pEngine->m_d->m_debuggerManagerAccess->breakHandler()->hasPendingBreakpoints())
m_pEngine->attemptBreakpointSynchronization();
return S_OK; return S_OK;
} }

View File

@@ -32,11 +32,18 @@
#include "cdbdebugengine_p.h" #include "cdbdebugengine_p.h"
#include "cdbdebugoutput.h" #include "cdbdebugoutput.h"
#include "cdbdebugeventcallback.h" #include "cdbdebugeventcallback.h"
#include "watchutils.h"
#include <QtCore/QRegExp> #include <QtCore/QRegExp>
#include <QtCore/QCoreApplication>
#include <QtCore/QTextStream>
enum { loadDebug = 0 }; enum { loadDebug = 0 };
static const char *dumperModuleNameC = "gdbmacros";
static const char *qtCoreModuleNameC = "QtCore";
static const ULONG waitTimeOutMS = 30000;
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
@@ -52,7 +59,7 @@ static bool allocDebuggeeMemory(CIDebugControl *ctl,
OutputRedirector redir(client, &stringHandler); OutputRedirector redir(client, &stringHandler);
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, allocCmd, errorMessage)) if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, allocCmd, errorMessage))
return false; return false;
// "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized // "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized
bool ok = false; bool ok = false;
const QString output = stringHandler.result(); const QString output = stringHandler.result();
const int lastBlank = output.lastIndexOf(QLatin1Char(' ')); const int lastBlank = output.lastIndexOf(QLatin1Char(' '));
@@ -61,6 +68,8 @@ static bool allocDebuggeeMemory(CIDebugControl *ctl,
if (ok) if (ok)
*address = addri; *address = addri;
} }
if (loadDebug)
qDebug() << Q_FUNC_INFO << '\n' << output << *address << ok;
if (!ok) { if (!ok) {
*errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output); *errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output);
return false; return false;
@@ -88,25 +97,6 @@ static bool createDebuggeeAscIIString(CIDebugControl *ctl,
return true; return true;
} }
// Locate 'qstrdup' in the (potentially namespaced) corelib. For some
// reason, the symbol is present in QtGui as well without type information.
static inline QString resolveStrdup(CIDebugSymbols *syms, QString *errorMessage)
{
QStringList matches;
const QString pattern = QLatin1String("*qstrdup");
const QRegExp corelibPattern(QLatin1String("QtCore[d]*4!"));
Q_ASSERT(corelibPattern.isValid());
if (!searchSymbols(syms, pattern, &matches, errorMessage))
return QString();
QStringList corelibStrdup = matches.filter(corelibPattern);
if (corelibStrdup.isEmpty()) {
*errorMessage = QString::fromLatin1("Unable to locate '%1' in '%2' (%3)").
arg(pattern, corelibPattern.pattern(), matches.join(QString(QLatin1Char(','))));
return QString();
}
return corelibStrdup.front();
}
// Load a library into the debuggee. Currently requires // Load a library into the debuggee. Currently requires
// the QtCored4.pdb file to be present as we need "qstrdup" // the QtCored4.pdb file to be present as we need "qstrdup"
// as dummy symbol. This is ok ATM since dumpers only // as dummy symbol. This is ok ATM since dumpers only
@@ -132,9 +122,10 @@ static bool debuggeeLoadLibrary(CIDebugControl *ctl,
// server, the debugger refuses to recognize it as a function. // server, the debugger refuses to recognize it as a function.
// Set up the call stack with a function of same signature (qstrdup) // Set up the call stack with a function of same signature (qstrdup)
// and change the call register to LoadLibraryA() before executing "g". // and change the call register to LoadLibraryA() before executing "g".
// Prepare call. // Prepare call: Locate 'qstrdup' in the (potentially namespaced) corelib. For some
const QString dummyFunc = resolveStrdup(syms, errorMessage); // reason, the symbol is present in QtGui as well without type information.
if (dummyFunc.isEmpty()) QString dummyFunc = QLatin1String("*qstrdup");
if (resolveSymbol(syms, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk)
return false; return false;
QString callCmd = QLatin1String(".call "); QString callCmd = QLatin1String(".call ");
callCmd += dummyFunc; callCmd += dummyFunc;
@@ -145,80 +136,293 @@ static bool debuggeeLoadLibrary(CIDebugControl *ctl,
return false; return false;
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QLatin1String("r eip=Kernel32!LoadLibraryA"), errorMessage)) if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QLatin1String("r eip=Kernel32!LoadLibraryA"), errorMessage))
return false; return false;
// This will hit a breakpoint // This will hit a breakpoint.
if (loadDebug)
qDebug() << " executing 'g'";
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QString(QLatin1Char('g')), errorMessage)) if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QString(QLatin1Char('g')), errorMessage))
return false;
const HRESULT hr = ctl->WaitForEvent(0, waitTimeOutMS);
if (FAILED(hr)) {
*errorMessage = msgComFailed("WaitForEvent", hr);
return false; return false;
// @Todo: We cannot evaluate output here as it is asynchronous }
return true; return true;
} }
CdbDumperHelper::CdbDumperHelper(CdbComInterfaces *cif) : // ------------------- CdbDumperHelper::DumperInputParameters
m_state(NotLoaded), struct CdbDumperHelper::DumperInputParameters {
m_cif(cif) DumperInputParameters(int protocolVersion_ = 1,
int token_ = 1,
quint64 Address_ = 0,
bool dumpChildren_ = false,
int extraInt0_ = 0,
int extraInt1_ = 0,
int extraInt2_ = 0,
int extraInt3_ = 0);
QString command(const QString &dumpFunction) const;
int protocolVersion;
int token;
quint64 address;
bool dumpChildren;
int extraInt0;
int extraInt1;
int extraInt2;
int extraInt3;
};
CdbDumperHelper::DumperInputParameters::DumperInputParameters(int protocolVersion_,
int token_,
quint64 address_,
bool dumpChildren_,
int extraInt0_,
int extraInt1_,
int extraInt2_,
int extraInt3_) :
protocolVersion(protocolVersion_),
token(token_),
address(address_),
dumpChildren(dumpChildren_),
extraInt0(extraInt0_),
extraInt1(extraInt1_),
extraInt2(extraInt2_),
extraInt3(extraInt3_)
{ {
} }
QString CdbDumperHelper::DumperInputParameters::command(const QString &dumpFunction) const
{
QString rc;
QTextStream str(&rc);
str.setIntegerBase(16);
str << ".call " << dumpFunction << '(' << protocolVersion << ',' << token << ',';
str.setIntegerBase(16);
str << "0x" << address;
str.setIntegerBase(10);
str << ',' << (dumpChildren ? 1 : 0) << ',' << extraInt0 << ',' << extraInt1
<< ',' << extraInt2 << ',' << extraInt3 << ')';
return rc;
}
// ------------------- CdbDumperHelper
CdbDumperHelper::CdbDumperHelper(CdbComInterfaces *cif) :
m_state(NotLoaded),
m_cif(cif),
m_inBufferAddress(0),
m_inBufferSize(0),
m_outBufferAddress(0),
m_outBufferSize(0),
m_buffer(0)
{
}
CdbDumperHelper::~CdbDumperHelper()
{
clearBuffer();
}
void CdbDumperHelper::clearBuffer()
{
if (m_buffer) {
delete [] m_buffer;
m_buffer = 0;
}
}
void CdbDumperHelper::reset(const QString &library, bool enabled) void CdbDumperHelper::reset(const QString &library, bool enabled)
{ {
m_library = library; m_library = library;
m_state = enabled ? NotLoaded : Disabled; m_state = enabled ? NotLoaded : Disabled;
m_dumpObjectSymbol = QLatin1String("qDumpObjectData440"); m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
m_errorMessage.clear(); m_knownTypes.clear();
m_qtVersion.clear();
m_qtNamespace.clear();
m_inBufferAddress = m_outBufferAddress = 0;
m_inBufferSize = m_outBufferSize = 0;
clearBuffer();
} }
bool CdbDumperHelper::moduleLoadHook(const QString &name, bool *ignoreNextBreakPoint) // Attempt to load and initialize dumpers, give feedback
// to user.
void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEngines *access)
{ {
*ignoreNextBreakPoint = false; enum Result { Failed, Succeeded, NoQtApp };
bool ok = true; // report failure only once
switch (m_state) { if (m_state != NotLoaded)
case Disabled: return;
break; manager->showStatusMessage(QCoreApplication::translate("CdbDumperHelper", "Loading dumpers..."), 10000);
case NotLoaded: const QString messagePrefix = QLatin1String("dumper:");
// Load once QtCore is there. QString message;
if (name.contains(QLatin1String("QtCore"))) { Result result = Failed;
if (loadDebug) do {
qDebug() << Q_FUNC_INFO << '\n' << name << m_state << executionStatusString(m_cif->debugControl); // Do we have Qt and are we already loaded by accident?
ok = debuggeeLoadLibrary(m_cif->debugControl, m_cif->debugClient, m_cif->debugSymbols, m_cif->debugDataSpaces, QStringList modules;
m_library, &m_errorMessage); if (!getModuleNameList(m_cif->debugSymbols, &modules, &message))
if (ok) { break;
m_state = Loading; if (modules.filter(QLatin1String(qtCoreModuleNameC)).isEmpty()) {
*ignoreNextBreakPoint = true; message = QCoreApplication::translate("CdbDumperHelper", "The debugger does not appear to be Qt application.");
} else { result = NoQtApp;
m_state = Failed;
}
if (loadDebug)
qDebug() << m_state << executionStatusString(m_cif->debugControl);
} }
break; // Make sure the dumper lib is loaded.
case Loading: if (modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty()) {
// Hurray, loaded. Now resolve the symbols we need // Try to load
if (name.contains(QLatin1String("gdbmacros"))) { if (!debuggeeLoadLibrary(m_cif->debugControl, m_cif->debugClient, m_cif->debugSymbols, m_cif->debugDataSpaces,
ok = resolveSymbols(&m_errorMessage); m_library, &message)) {
if (ok) { break;
m_state = Loaded;
} else {
m_state = Failed;
} }
} else {
access->showDebuggerOutput(messagePrefix, QCoreApplication::translate("CdbDumperHelper", "The dumper module appears to be already loaded."));
} }
break; // Resolve symbols and do call to get types
case Loaded: if (!resolveSymbols(&message))
break; break;
if (!getKnownTypes(&message))
break;
message = QCoreApplication::translate("CdbDumperHelper", "Dumper library '%1' loaded.").arg(m_library);
result = Succeeded;
} while (false);
// eval state and notify user
switch (result) {
case Failed: case Failed:
message = QCoreApplication::translate("CdbDumperHelper", "The dumper library '%1' could not be loaded:\n%2").arg(m_library, message);
access->showDebuggerOutput(messagePrefix, message);
access->showQtDumperLibraryWarning(message);
m_state = Disabled;
break; break;
}; case Succeeded:
return ok; access->showDebuggerOutput(messagePrefix, message);
access->showDebuggerOutput(messagePrefix, statusMessage());
m_state = Loaded;
break;
case NoQtApp:
access->showDebuggerOutput(messagePrefix, message);
m_state = Disabled;
break;
}
if (loadDebug)
qDebug() << Q_FUNC_INFO << "\n<" << result << '>' << m_state << message;
}
QString CdbDumperHelper::statusMessage() const
{
const QString nameSpace = m_qtNamespace.isEmpty() ? QCoreApplication::translate("CdbDumperHelper", "<none>") : m_qtNamespace;
return QCoreApplication::translate("CdbDumperHelper",
"%n known types, Qt version: %1, Qt namespace: %2",
0, QCoreApplication::CodecForTr,
m_knownTypes.size()).arg(m_qtVersion, nameSpace);
}
// Retrieve address and optionally size of a symbol.
static inline bool getSymbolAddress(CIDebugSymbols *sg,
const QString &name,
quint64 *address,
ULONG *size /* = 0*/,
QString *errorMessage)
{
// Get address
HRESULT hr = sg->GetOffsetByNameWide(name.utf16(), address);
if (FAILED(hr)) {
*errorMessage = msgComFailed("GetOffsetByNameWide", hr);
return false;
}
// Get size. Even works for arrays
if (size) {
ULONG64 moduleAddress;
ULONG type;
hr = sg->GetOffsetTypeId(*address, &type, &moduleAddress);
if (FAILED(hr)) {
*errorMessage = msgComFailed("GetOffsetTypeId", hr);
return false;
}
hr = sg->GetTypeSize(moduleAddress, type, size);
if (FAILED(hr)) {
*errorMessage = msgComFailed("GetTypeSize", hr);
return false;
}
} // size desired
return true;
} }
bool CdbDumperHelper::resolveSymbols(QString *errorMessage) bool CdbDumperHelper::resolveSymbols(QString *errorMessage)
{ {
// Resolve the symbols we need // Resolve the symbols we need (potentially namespaced).
m_dumpObjectSymbol = QLatin1String("qDumpObjectData440"); // There is a 'qDumpInBuffer' in QtCore as well.
const bool rc = resolveSymbol(m_cif->debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk; m_dumpObjectSymbol = QLatin1String("*qDumpObjectData440");
QString inBufferSymbol = QLatin1String("*qDumpInBuffer");
QString outBufferSymbol = QLatin1String("*qDumpOutBuffer");
const QString dumperModuleName = QLatin1String(dumperModuleNameC);
bool rc = resolveSymbol(m_cif->debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk
&& resolveSymbol(m_cif->debugSymbols, dumperModuleName, &inBufferSymbol, errorMessage) == ResolveSymbolOk
&& resolveSymbol(m_cif->debugSymbols, dumperModuleName, &outBufferSymbol, errorMessage) == ResolveSymbolOk;
if (!rc)
return false;
// Determine buffer addresses, sizes and alloc buffer
rc = getSymbolAddress(m_cif->debugSymbols, inBufferSymbol, &m_inBufferAddress, &m_inBufferSize, errorMessage)
&& getSymbolAddress(m_cif->debugSymbols, outBufferSymbol, &m_outBufferAddress, &m_outBufferSize, errorMessage);
if (!rc)
return false;
m_buffer = new char[qMax(m_inBufferSize, m_outBufferSize)];
if (loadDebug) if (loadDebug)
qDebug() << Q_FUNC_INFO << '\n' << rc << m_dumpObjectSymbol; qDebug().nospace() << Q_FUNC_INFO << '\n' << rc << m_dumpObjectSymbol
return rc; << " buffers at 0x" << QString::number(m_inBufferAddress, 16) << ','
<< m_inBufferSize << "; 0x"
<< QString::number(m_outBufferAddress, 16) << ',' << m_outBufferSize << '\n';
return true;
}
bool CdbDumperHelper::getKnownTypes(QString *errorMessage)
{
QByteArray output;
if (!callDumper(DumperInputParameters(1), &output, errorMessage)) {
return false;
}
if (!parseQueryDumperOutput(output, &m_knownTypes, &m_qtVersion, &m_qtNamespace)) {
*errorMessage = QString::fromLatin1("Unable to parse the dumper output: '%1'").arg(QString::fromAscii(output));
}
if (loadDebug)
qDebug() << Q_FUNC_INFO << m_knownTypes << m_qtVersion << m_qtNamespace;
return true;
}
bool CdbDumperHelper::callDumper(const DumperInputParameters &p, QByteArray *output, QString *errorMessage)
{
IgnoreDebugEventCallback devNull;
EventCallbackRedirector eventRedir(m_cif->debugClient, &devNull);
const QString callCmd = p.command(m_dumpObjectSymbol);
// Set up call and a temporary breakpoint after it.
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, callCmd, errorMessage))
return false;
// Go and filter away break event
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, QString(QLatin1Char('g')), errorMessage))
return false;
HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS);
if (FAILED(hr)) {
*errorMessage = msgComFailed("WaitForEvent", hr);
return false;
}
// Read output
hr = m_cif->debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0);
if (FAILED(hr)) {
*errorMessage = msgComFailed("ReadVirtual", hr);
return false;
}
// see QDumper implementation
const char result = m_buffer[0];
switch (result) {
case 't':
break;
case '+':
*errorMessage = QString::fromLatin1("Dumper call '%1' resulted in output overflow.").arg(callCmd);
return false;
case 'f':
*errorMessage = QString::fromLatin1("Dumper call '%1' failed.").arg(callCmd);
return false;
default:
*errorMessage = QString::fromLatin1("Dumper call '%1' failed ('%2').").arg(callCmd).arg(QLatin1Char(result));
return false;
}
*output = QByteArray(m_buffer + 1);
return true;
} }
} // namespace Internal } // namespace Internal

View File

@@ -30,55 +30,77 @@
#ifndef CDBDUMPERHELPER_H #ifndef CDBDUMPERHELPER_H
#define CDBDUMPERHELPER_H #define CDBDUMPERHELPER_H
#include <QtCore/QString> #include <QtCore/QStringList>
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
struct CdbComInterfaces; struct CdbComInterfaces;
class IDebuggerManagerAccessForEngines;
class DebuggerManager;
// For code clarity, all the stuff related to custom dumpers /* For code clarity, all the stuff related to custom dumpers
// goes here. * goes here.
// "Custom dumper" is a library compiled against the current * "Custom dumper" is a library compiled against the current
// Qt containing functions to evaluate values of Qt classes * Qt containing functions to evaluate values of Qt classes
// (such as QString, taking pointers to their addresses). * (such as QString, taking pointers to their addresses).
// The library must be loaded into the debuggee. * The library must be loaded into the debuggee.
* Loading the dumpers requires making the debuggee call functions
* (LoadLibrary() and the dumper functions). This only works if the
* debuggee is in a 'well-defined' breakpoint state (such as at 'main()').
* Calling the load functions from an IDebugEvent callback causes
* WaitForEvent() to fail with unknown errors. Calling the load functions from an
* non 'well-defined' (arbitrary) breakpoint state will cause LoadLibrary
* to trigger an access violations.
* Currently, we call the load function when stopping at 'main()' for which
* we set a temporary break point if the user does not want to stop there. */
class CdbDumperHelper class CdbDumperHelper
{ {
Q_DISABLE_COPY(CdbDumperHelper)
public: public:
enum State { enum State {
Disabled, Disabled,
NotLoaded, NotLoaded,
Loading,
Loaded, Loaded,
Failed Failed
}; };
explicit CdbDumperHelper(CdbComInterfaces *cif); explicit CdbDumperHelper(CdbComInterfaces *cif);
~CdbDumperHelper();
State state() const { return m_state; }
operator bool() const { return m_state == Loaded; }
// Call before starting the debugger // Call before starting the debugger
void reset(const QString &library, bool enabled); void reset(const QString &library, bool enabled);
// Call from the module loaded event handler. // Call in a temporary breakpoint state to actually load.
// It will load the dumper library and resolve the required symbols void load(DebuggerManager *manager, IDebuggerManagerAccessForEngines *access);
// when appropriate.
bool moduleLoadHook(const QString &name, bool *ignoreNextBreakPoint);
State state() const { return m_state; }
QString errorMessage() const { return m_errorMessage; }
QString library() const { return m_library; }
private: private:
struct DumperInputParameters;
void clearBuffer();
bool resolveSymbols(QString *errorMessage); bool resolveSymbols(QString *errorMessage);
bool getKnownTypes(QString *errorMessage);
bool callDumper(const DumperInputParameters &p, QByteArray *output, QString *errorMessage);
inline QString statusMessage() const;
State m_state; State m_state;
CdbComInterfaces *m_cif; CdbComInterfaces *m_cif;
QString m_library; QString m_library;
QString m_dumpObjectSymbol; QString m_dumpObjectSymbol;
QString m_errorMessage; QStringList m_knownTypes;
QString m_qtVersion;
QString m_qtNamespace;
quint64 m_inBufferAddress;
unsigned long m_inBufferSize;
quint64 m_outBufferAddress;
unsigned long m_outBufferSize;
char *m_buffer;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -32,25 +32,47 @@
#include "cdbdebugengine_p.h" #include "cdbdebugengine_p.h"
#include <QtCore/QFileInfo> #include <QtCore/QFileInfo>
#include <QtCore/QRegExp>
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
bool getModuleList(CIDebugSymbols *syms, QList<Module> *modules, QString *errorMessage) static inline bool getModuleCount(CIDebugSymbols *syms, ULONG *count, QString *errorMessage)
{ {
modules->clear(); *count = 0;
ULONG loadedCount, unloadedCount; ULONG loadedCount, unloadedCount;
HRESULT hr = syms->GetNumberModules(&loadedCount, &unloadedCount); const HRESULT hr = syms->GetNumberModules(&loadedCount, &unloadedCount);
if (FAILED(hr)) { if (FAILED(hr)) {
*errorMessage= msgComFailed("GetNumberModules", hr); *errorMessage= msgComFailed("GetNumberModules", hr);
return false; return false;
} }
// retrieve array of parameters *count = loadedCount + unloadedCount;
const ULONG count = loadedCount + unloadedCount; return true;
}
bool getModuleNameList(CIDebugSymbols *syms, QStringList *modules, QString *errorMessage)
{
ULONG count;
modules->clear();
if (!getModuleCount(syms, &count, errorMessage))
return false;
WCHAR wszBuf[MAX_PATH];
for (ULONG m = 0; m < count; m++)
if (SUCCEEDED(syms->GetModuleNameStringWide(DEBUG_MODNAME_IMAGE, m, 0, wszBuf, MAX_PATH - 1, 0)))
modules->push_back(QString::fromUtf16(wszBuf));
return true;
}
bool getModuleList(CIDebugSymbols *syms, QList<Module> *modules, QString *errorMessage)
{
ULONG count;
modules->clear();
if (!getModuleCount(syms, &count, errorMessage))
return false;
QVector<DEBUG_MODULE_PARAMETERS> parameters(count); QVector<DEBUG_MODULE_PARAMETERS> parameters(count);
DEBUG_MODULE_PARAMETERS *parmPtr = &(*parameters.begin()); DEBUG_MODULE_PARAMETERS *parmPtr = &(*parameters.begin());
memset(parmPtr, 0, sizeof(DEBUG_MODULE_PARAMETERS) * count); memset(parmPtr, 0, sizeof(DEBUG_MODULE_PARAMETERS) * count);
hr = syms->GetModuleParameters(count, 0, 0u, parmPtr); HRESULT hr = syms->GetModuleParameters(count, 0, 0u, parmPtr);
// E_INVALIDARG indicates 'Partial results' according to docu // E_INVALIDARG indicates 'Partial results' according to docu
if (FAILED(hr) && hr != E_INVALIDARG) { if (FAILED(hr) && hr != E_INVALIDARG) {
*errorMessage= msgComFailed("GetModuleParameters", hr); *errorMessage= msgComFailed("GetModuleParameters", hr);
@@ -67,7 +89,7 @@ bool getModuleList(CIDebugSymbols *syms, QList<Module> *modules, QString *errorM
&& (p.SymbolType != DEBUG_SYMTYPE_NONE); && (p.SymbolType != DEBUG_SYMTYPE_NONE);
module.startAddress = hexPrefix + QString::number(p.Base, 16); module.startAddress = hexPrefix + QString::number(p.Base, 16);
module.endAddress = hexPrefix + QString::number((p.Base + p.Size), 16); module.endAddress = hexPrefix + QString::number((p.Base + p.Size), 16);
hr = syms ->GetModuleNameStringWide(DEBUG_MODNAME_IMAGE, m, 0, wszBuf, MAX_PATH - 1, 0); hr = syms->GetModuleNameStringWide(DEBUG_MODNAME_IMAGE, m, 0, wszBuf, MAX_PATH - 1, 0);
if (FAILED(hr) && hr != E_INVALIDARG) { if (FAILED(hr) && hr != E_INVALIDARG) {
*errorMessage= msgComFailed("GetModuleNameStringWide", hr); *errorMessage= msgComFailed("GetModuleNameStringWide", hr);
return false; return false;
@@ -111,10 +133,10 @@ bool searchSymbols(CIDebugSymbols *syms, const QString &pattern,
return true; return true;
} }
// Add missing the module specifier: "main" -> "project!main" // Helper for the resolveSymbol overloads.
static ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol,
ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol, QStringList *matches,
QString *errorMessage) QString *errorMessage)
{ {
// Is it an incomplete symbol? // Is it an incomplete symbol?
if (symbol->contains(QLatin1Char('!'))) if (symbol->contains(QLatin1Char('!')))
@@ -123,20 +145,63 @@ ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol,
if (*symbol == QLatin1String("qMain")) if (*symbol == QLatin1String("qMain"))
*symbol = QLatin1String("main"); *symbol = QLatin1String("main");
// resolve // resolve
QStringList matches; if (!searchSymbols(syms, *symbol, matches, errorMessage))
if (!searchSymbols(syms, *symbol, &matches, errorMessage))
return ResolveSymbolError; return ResolveSymbolError;
if (matches.empty()) if (matches->empty())
return ResolveSymbolNotFound; return ResolveSymbolNotFound;
*symbol = matches.front(); *symbol = matches->front();
if (matches.size() > 1) { if (matches->size() > 1) {
*errorMessage = QString::fromLatin1("Ambiguous symbol '%1': %2"). *errorMessage = QString::fromLatin1("Ambiguous symbol '%1': %2").
arg(*symbol, matches.join(QString(QLatin1Char(' ')))); arg(*symbol, matches->join(QString(QLatin1Char(' '))));
return ResolveSymbolAmbiguous; return ResolveSymbolAmbiguous;
} }
return ResolveSymbolOk; return ResolveSymbolOk;
} }
// Add missing the module specifier: "main" -> "project!main"
ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol,
QString *errorMessage)
{
QStringList matches;
return resolveSymbol(syms, symbol, &matches, errorMessage);
}
ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, const QString &pattern, QString *symbol, QString *errorMessage)
{
QStringList matches;
const ResolveSymbolResult r1 = resolveSymbol(syms, symbol, &matches, errorMessage);
switch (r1) {
case ResolveSymbolOk:
case ResolveSymbolNotFound:
case ResolveSymbolError:
return r1;
case ResolveSymbolAmbiguous:
break;
}
// Filter out
errorMessage->clear();
const QRegExp re(pattern);
if (!re.isValid()) {
*errorMessage = QString::fromLatin1("Internal error: Invalid pattern '%1'.").arg(pattern);
return ResolveSymbolError;
}
const QStringList filteredMatches = matches.filter(re);
if (filteredMatches.size() == 1) {
*symbol = filteredMatches.front();
return ResolveSymbolOk;
}
// something went wrong
const QString matchesString = matches.join(QString(QLatin1Char(',')));
if (filteredMatches.empty()) {
*errorMessage = QString::fromLatin1("None of symbols '%1' found for '%2' matches '%3'.").
arg(matchesString, *symbol, pattern);
return ResolveSymbolNotFound;
}
*errorMessage = QString::fromLatin1("Ambiguous match of symbols '%1' found for '%2' (%3)").
arg(matchesString, *symbol, pattern);
return ResolveSymbolAmbiguous;
}
// List symbols of a module // List symbols of a module
bool getModuleSymbols(CIDebugSymbols *syms, const QString &moduleName, bool getModuleSymbols(CIDebugSymbols *syms, const QString &moduleName,
QList<Symbol> *symbols, QString *errorMessage) QList<Symbol> *symbols, QString *errorMessage)

View File

@@ -30,8 +30,7 @@
#ifndef CDBMODULES_H #ifndef CDBMODULES_H
#define CDBMODULES_H #define CDBMODULES_H
#include <QtCore/QList> #include <QtCore/QStringList>
#include <QtCore/QString>
#include "cdbcom.h" #include "cdbcom.h"
@@ -42,7 +41,8 @@ class Module;
class Symbol; class Symbol;
bool getModuleList(CIDebugSymbols *syms, QList<Module> *modules, QString *errorMessage); bool getModuleList(CIDebugSymbols *syms, QList<Module> *modules, QString *errorMessage);
// Search symbols matching a pattern bool getModuleNameList(CIDebugSymbols *syms, QStringList *modules, QString *errorMessage);
// Search symbols matching a pattern. Does not filter on module names.
bool searchSymbols(CIDebugSymbols *syms, const QString &pattern, bool searchSymbols(CIDebugSymbols *syms, const QString &pattern,
QStringList *matches, QString *errorMessage); QStringList *matches, QString *errorMessage);
@@ -52,8 +52,12 @@ bool searchSymbols(CIDebugSymbols *syms, const QString &pattern,
enum ResolveSymbolResult { ResolveSymbolOk, ResolveSymbolAmbiguous, enum ResolveSymbolResult { ResolveSymbolOk, ResolveSymbolAmbiguous,
ResolveSymbolNotFound, ResolveSymbolError }; ResolveSymbolNotFound, ResolveSymbolError };
// Resolve a symbol that is unique to all modules
ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol, QString *errorMessage); ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol, QString *errorMessage);
// Resolve symbol overload with an additional regexp pattern to filter on modules.
ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, const QString &pattern, QString *symbol, QString *errorMessage);
// List symbols of a module // List symbols of a module
bool getModuleSymbols(CIDebugSymbols *syms, const QString &moduleName, bool getModuleSymbols(CIDebugSymbols *syms, const QString &moduleName,
QList<Symbol> *symbols, QString *errorMessage); QList<Symbol> *symbols, QString *errorMessage);

View File

@@ -145,6 +145,7 @@ private:
friend class CdbDebugEventCallback; friend class CdbDebugEventCallback;
friend class ScriptEngine; friend class ScriptEngine;
friend struct CdbDebugEnginePrivate; friend struct CdbDebugEnginePrivate;
friend class CdbDumperHelper;
// called from the engines after successful startup // called from the engines after successful startup
virtual void notifyInferiorStopRequested() = 0; virtual void notifyInferiorStopRequested() = 0;
@@ -171,6 +172,11 @@ private:
virtual void reloadModules() = 0; virtual void reloadModules() = 0;
virtual void reloadSourceFiles() = 0; virtual void reloadSourceFiles() = 0;
virtual void reloadRegisters() = 0; virtual void reloadRegisters() = 0;
virtual bool qtDumperLibraryEnabled() const = 0;
virtual QString qtDumperLibraryName() const = 0;
virtual void showQtDumperLibraryWarning(const QString &details = QString()) = 0;
}; };
@@ -252,10 +258,6 @@ public slots:
void showStatusMessage(const QString &msg, int timeout = -1); // -1 forever void showStatusMessage(const QString &msg, int timeout = -1); // -1 forever
bool qtDumperLibraryEnabled() const;
QString qtDumperLibraryName() const;
void showQtDumperLibraryWarning(const QString &details);
private slots: private slots:
void showDebuggerOutput(const QString &prefix, const QString &msg); void showDebuggerOutput(const QString &prefix, const QString &msg);
void showDebuggerInput(const QString &prefix, const QString &msg); void showDebuggerInput(const QString &prefix, const QString &msg);
@@ -307,6 +309,10 @@ private:
QList<QDockWidget*> dockWidgets() const { return m_dockWidgets; } QList<QDockWidget*> dockWidgets() const { return m_dockWidgets; }
void createDockWidgets(); void createDockWidgets();
virtual bool qtDumperLibraryEnabled() const;
virtual QString qtDumperLibraryName() const;
virtual void showQtDumperLibraryWarning(const QString &details = QString());
// //
// internal implementation // internal implementation
// //

View File

@@ -4135,16 +4135,16 @@ void GdbEngine::tryLoadDebuggingHelpers()
PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS"); PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
m_debuggingHelperState = DebuggingHelperUnavailable; m_debuggingHelperState = DebuggingHelperUnavailable;
if (!q->qtDumperLibraryEnabled()) if (!qq->qtDumperLibraryEnabled())
return; return;
const QString lib = q->qtDumperLibraryName(); const QString lib = qq->qtDumperLibraryName();
//qDebug() << "DUMPERLIB: " << lib; //qDebug() << "DUMPERLIB: " << lib;
// @TODO: same in CDB engine... // @TODO: same in CDB engine...
const QFileInfo fi(lib); const QFileInfo fi(lib);
if (!fi.exists()) { if (!fi.exists()) {
const QString msg = tr("The dumper library '%1' does not exist.").arg(lib); const QString msg = tr("The dumper library '%1' does not exist.").arg(lib);
debugMessage(msg); debugMessage(msg);
q->showQtDumperLibraryWarning(msg); qq->showQtDumperLibraryWarning(msg);
return; return;
} }

View File

@@ -299,5 +299,58 @@ QString sizeofTypeExpression(const QString &type)
return QLatin1String("sizeof(") + gdbQuoteTypes(type) + QLatin1Char(')'); return QLatin1String("sizeof(") + gdbQuoteTypes(type) + QLatin1Char(')');
} }
/* Parse 'query' (1) protocol response of the custom dumpers:
* "'dumpers=["QByteArray","QDateTime",..."std::basic_string",],
* qtversion=["4","5","1"],namespace="""' */
bool parseQueryDumperOutput(const QByteArray &a, QStringList *types, QString *qtVersion, QString *qtNamespace)
{
types->clear();
qtVersion->clear();
qtNamespace->clear();
const QChar equals = QLatin1Char('=');
const QChar doubleQuote = QLatin1Char('"');
const QChar openingBracket = QLatin1Char('[');
const QChar closingBracket = QLatin1Char(']');
const QString dumperKey = QLatin1String("dumpers");
const QString versionKey = QLatin1String("qtversion");
const QString namespaceKey = QLatin1String("namespace");
const QString s = QString::fromLatin1(a);
const int size = a.size();
for (int pos = 0; pos < size; ) {
// split into keyword / value pairs
const int equalsPos = s.indexOf(equals, pos);
if (equalsPos == -1)
break;
const QStringRef keyword = s.midRef(pos, equalsPos - pos);
// Array or flat value. Cut out value without delimiters
int valuePos = equalsPos + 1;
const QChar endChar = s.at(valuePos) == doubleQuote ? doubleQuote : closingBracket;
valuePos++;
int endValuePos = s.indexOf(endChar, valuePos);
if (endValuePos == -1)
return false;
QString value = s.mid(valuePos, endValuePos - valuePos);
pos = endValuePos;
// Evaluate
if (keyword == namespaceKey) {
*qtNamespace = value;
} else {
if (keyword == versionKey) {
*qtVersion = value.remove(doubleQuote).replace(QLatin1Char(','), QLatin1Char('.'));
} else {
if (keyword == dumperKey) {
*types = value.remove(doubleQuote).split(QLatin1Char(','));
}
}
}
// find next keyword
while (pos < size && !s.at(pos).isLetterOrNumber())
pos++;
}
return true;
}
} }
} }

View File

@@ -34,6 +34,7 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QString; class QString;
class QByteArray;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace Debugger { namespace Debugger {
@@ -63,6 +64,8 @@ QString extractTypeFromPTypeOutput(const QString &str);
bool isIntOrFloatType(const QString &type); bool isIntOrFloatType(const QString &type);
QString sizeofTypeExpression(const QString &type); QString sizeofTypeExpression(const QString &type);
// Parse 'query' (1) protocol response of the custom dumpers
bool parseQueryDumperOutput(const QByteArray &a, QStringList *types, QString *qtVersion, QString *qtNamespace);
} // namespace Internal } // namespace Internal
} // namespace Debugger } // namespace Debugger