forked from qt-creator/qt-creator
Make CDB load custom dumpers.
Load in a 'well-defined' (temporary) breakpoint at main().
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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, ¤tThreadId))) {
|
||||||
|
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()
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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, ¤tThreadId)))
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user