diff --git a/src/plugins/debugger/cdb/cdb.pri b/src/plugins/debugger/cdb/cdb.pri index 7e78763dfe538592e3294d4f28f7026031dd6a84..c11f3846bdea8a565cdafb657dc399c0fc67ed3a 100644 --- a/src/plugins/debugger/cdb/cdb.pri +++ b/src/plugins/debugger/cdb/cdb.pri @@ -37,7 +37,8 @@ HEADERS += \ $$PWD/cdbmodules.h \ $$PWD/cdbassembler.h \ $$PWD/cdboptions.h \ - $$PWD/cdboptionspage.h + $$PWD/cdboptionspage.h \ + $$PWD/cdbdumperhelper.h SOURCES += \ $$PWD/cdbdebugengine.cpp \ @@ -49,7 +50,8 @@ SOURCES += \ $$PWD/cdbmodules.cpp \ $$PWD/cdbassembler.cpp \ $$PWD/cdboptions.cpp \ - $$PWD/cdboptionspage.cpp + $$PWD/cdboptionspage.cpp \ + $$PWD/cdbdumperhelper.cpp FORMS += $$PWD/cdboptionspagewidget.ui diff --git a/src/plugins/debugger/cdb/cdbassembler.cpp b/src/plugins/debugger/cdb/cdbassembler.cpp index 4cb59c7d69f018d7d9f721c8b17cc5a3fa267e45..ae030578c9aba6f38e15b9dda062f47b706335fe 100644 --- a/src/plugins/debugger/cdb/cdbassembler.cpp +++ b/src/plugins/debugger/cdb/cdbassembler.cpp @@ -212,14 +212,11 @@ bool dissassemble(IDebugClient5 *client, // in assembler code). We build a complete string first as line breaks // may occur in-between messages. StringOutputHandler stringHandler; - IDebugOutputCallbacksWide *oldHandler = CdbDebugOutputBase::getOutputCallback(client); - client->SetOutputCallbacksWide(&stringHandler); + OutputRedirector redir(client, &stringHandler); // For some reason, we need to output to "all clients" const HRESULT hr = ctl->OutputDisassemblyLines(DEBUG_OUTCTL_ALL_CLIENTS, beforeLines, beforeLines + afterLines, offset, flags, 0, 0, 0, 0); - client->SetOutputCallbacksWide(oldHandler); - if (FAILED(hr)) { *errorMessage= QString::fromLatin1("Unable to dissamble at 0x%1: %2"). arg(QString::number(offset, 16), msgComFailed("OutputDisassemblyLines", hr)); diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index b210186c0f563c8ae38d48d9f4b26fbfd10be0f6..35028561a083af58b9b66a1c51535e1a9352391c 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -188,19 +188,7 @@ bool DebuggerEngineLibrary::init(const QString &path, QString *errorMessage) return true; } -// A class that sets an expression syntax on the debug control while in scope. -// Can be nested as it checks for the old value. -class SyntaxSetter { - Q_DISABLE_COPY(SyntaxSetter) -public: - explicit inline SyntaxSetter(IDebugControl4 *ctl, ULONG desiredSyntax); - inline ~SyntaxSetter(); -private: - const ULONG m_desiredSyntax; - IDebugControl4 *m_ctl; - ULONG m_oldSyntax; -}; - +// ----- SyntaxSetter SyntaxSetter::SyntaxSetter(IDebugControl4 *ctl, ULONG desiredSyntax) : m_desiredSyntax(desiredSyntax), m_ctl(ctl) @@ -216,6 +204,17 @@ SyntaxSetter::~SyntaxSetter() m_ctl->SetExpressionSyntax(m_oldSyntax); } +// CdbComInterfaces +CdbComInterfaces::CdbComInterfaces() : + debugClient(0), + debugControl(0), + debugSystemObjects(0), + debugSymbols(0), + debugRegisters(0), + debugDataSpaces(0) +{ +} + // --- CdbDebugEnginePrivate CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent, @@ -225,13 +224,9 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent, m_hDebuggeeProcess(0), m_hDebuggeeThread(0), m_breakEventMode(BreakEventHandle), + m_dumper(&m_cif), m_watchTimer(-1), m_debugEventCallBack(engine), - m_pDebugClient(0), - m_pDebugControl(0), - m_pDebugSystemObjects(0), - m_pDebugSymbols(0), - m_pDebugRegisters(0), m_engine(engine), m_debuggerManager(parent), m_debuggerManagerAccess(parent->engineInterface()), @@ -250,42 +245,49 @@ bool CdbDebugEnginePrivate::init(QString *errorMessage) // Initialize the COM interfaces HRESULT hr; - hr = lib.debugCreate( __uuidof(IDebugClient5), reinterpret_cast<void**>(&m_pDebugClient)); + hr = lib.debugCreate( __uuidof(IDebugClient5), reinterpret_cast<void**>(&m_cif.debugClient)); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Creation of IDebugClient5 failed: %1").arg(msgDebugEngineComResult(hr)); return false; } - m_pDebugClient->SetOutputCallbacksWide(&m_debugOutputCallBack); - m_pDebugClient->SetEventCallbacks(&m_debugEventCallBack); + m_cif.debugClient->SetOutputCallbacksWide(&m_debugOutputCallBack); + m_cif.debugClient->SetEventCallbacksWide(&m_debugEventCallBack); - hr = lib.debugCreate( __uuidof(IDebugControl4), reinterpret_cast<void**>(&m_pDebugControl)); + hr = lib.debugCreate( __uuidof(IDebugControl4), reinterpret_cast<void**>(&m_cif.debugControl)); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Creation of IDebugControl4 failed: %1").arg(msgDebugEngineComResult(hr)); return false; } - m_pDebugControl->SetCodeLevel(DEBUG_LEVEL_SOURCE); + m_cif.debugControl->SetCodeLevel(DEBUG_LEVEL_SOURCE); - hr = lib.debugCreate( __uuidof(IDebugSystemObjects4), reinterpret_cast<void**>(&m_pDebugSystemObjects)); + hr = lib.debugCreate( __uuidof(IDebugSystemObjects4), reinterpret_cast<void**>(&m_cif.debugSystemObjects)); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Creation of IDebugSystemObjects4 failed: %1").arg(msgDebugEngineComResult(hr)); return false; } - hr = lib.debugCreate( __uuidof(IDebugSymbols3), reinterpret_cast<void**>(&m_pDebugSymbols)); + hr = lib.debugCreate( __uuidof(IDebugSymbols3), reinterpret_cast<void**>(&m_cif.debugSymbols)); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Creation of IDebugSymbols3 failed: %1").arg(msgDebugEngineComResult(hr)); return false; } - hr = lib.debugCreate( __uuidof(IDebugRegisters2), reinterpret_cast<void**>(&m_pDebugRegisters)); + hr = lib.debugCreate( __uuidof(IDebugRegisters2), reinterpret_cast<void**>(&m_cif.debugRegisters)); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Creation of IDebugRegisters2 failed: %1").arg(msgDebugEngineComResult(hr)); return false; } + + hr = lib.debugCreate( __uuidof(IDebugDataSpaces4), reinterpret_cast<void**>(&m_cif.debugDataSpaces)); + if (FAILED(hr)) { + *errorMessage = QString::fromLatin1("Creation of IDebugDataSpaces4 failed: %1").arg(msgDebugEngineComResult(hr)); + return false; + } + if (debugCDB) - qDebug() << QString::fromLatin1("CDB Initialization succeeded, interrupt time out %1s.").arg(getInterruptTimeOutSecs(m_pDebugControl)); + qDebug() << QString::fromLatin1("CDB Initialization succeeded, interrupt time out %1s.").arg(getInterruptTimeOutSecs(m_cif.debugControl)); return true; } @@ -303,16 +305,18 @@ IDebuggerEngine *CdbDebugEngine::create(DebuggerManager *parent, CdbDebugEnginePrivate::~CdbDebugEnginePrivate() { cleanStackTrace(); - if (m_pDebugClient) - m_pDebugClient->Release(); - if (m_pDebugControl) - m_pDebugControl->Release(); - if (m_pDebugSystemObjects) - m_pDebugSystemObjects->Release(); - if (m_pDebugSymbols) - m_pDebugSymbols->Release(); - if (m_pDebugRegisters) - m_pDebugRegisters->Release(); + if (m_cif.debugClient) + m_cif.debugClient->Release(); + if (m_cif.debugControl) + m_cif.debugControl->Release(); + if (m_cif.debugSystemObjects) + m_cif.debugSystemObjects->Release(); + if (m_cif.debugSymbols) + m_cif.debugSymbols->Release(); + if (m_cif.debugRegisters) + m_cif.debugRegisters->Release(); + if (m_cif.debugDataSpaces) + m_cif.debugDataSpaces->Release(); } void CdbDebugEnginePrivate::clearForRun() @@ -395,6 +399,18 @@ void CdbDebugEnginePrivate::clearDisplay() bool CdbDebugEngine::startDebugger() { m_d->clearDisplay(); + // Figure out dumper. @TODO: same in gdb... + bool dumperEnabled = false && m_d->m_debuggerManager->qtDumperLibraryEnabled(); + const QString dumperLibName = QDir::toNativeSeparators(m_d->m_debuggerManager->qtDumperLibraryName()); + if (dumperEnabled) { + const QFileInfo fi(dumperLibName); + if (!fi.isFile()) { + const QString msg = tr("The dumper library '%1' does not exist.").arg(dumperLibName); + m_d->m_debuggerManager->showQtDumperLibraryWarning(msg); + dumperEnabled = false; + } + } + m_d->m_dumper.reset(dumperLibName, dumperEnabled); m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1); QString errorMessage; bool rc = false; @@ -435,7 +451,7 @@ bool CdbDebugEngine::startAttachDebugger(qint64 pid, QString *errorMessage) { // Need to aatrach invasively, otherwise, no notification signals // for for CreateProcess/ExitProcess occur. - const HRESULT hr = m_d->m_pDebugClient->AttachProcess(NULL, pid, + const HRESULT hr = m_d->m_cif.debugClient->AttachProcess(NULL, pid, DEBUG_ATTACH_INVASIVE_RESUME_PROCESS); if (debugCDB) qDebug() << "Attaching to " << pid << " returns " << hr; @@ -461,10 +477,10 @@ bool CdbDebugEngine::startDebuggerWithExecutable(DebuggerStartMode sm, QString * qDebug() << Q_FUNC_INFO <<filename; const QFileInfo fi(filename); - m_d->m_pDebugSymbols->AppendImagePathWide(QDir::toNativeSeparators(fi.absolutePath()).utf16()); - //m_pDebugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS); - m_d->m_pDebugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS); - //m_pDebugSymbols->AddSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS | SYMOPT_NO_IMAGE_SEARCH); + m_d->m_cif.debugSymbols->AppendImagePathWide(QDir::toNativeSeparators(fi.absolutePath()).utf16()); + //m_cif.debugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS); + m_d->m_cif.debugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS); + //m_cif.debugSymbols->AddSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS | SYMOPT_NO_IMAGE_SEARCH); // TODO console const QString cmd = Core::Utils::AbstractProcess::createWinCommandline(filename, m_d->m_debuggerManager->m_processArgs); @@ -476,7 +492,7 @@ bool CdbDebugEngine::startDebuggerWithExecutable(DebuggerStartMode sm, QString * envData = Core::Utils::AbstractProcess::createWinEnvironment(Core::Utils::AbstractProcess::fixWinEnvironment(m_d->m_debuggerManager->m_environment)); env = reinterpret_cast<PCWSTR>(envData.data()); } - const HRESULT hr = m_d->m_pDebugClient->CreateProcess2Wide(NULL, + const HRESULT hr = m_d->m_cif.debugClient->CreateProcess2Wide(NULL, const_cast<PWSTR>(cmd.utf16()), &dbgopts, sizeof(dbgopts), @@ -490,6 +506,7 @@ bool CdbDebugEngine::startDebuggerWithExecutable(DebuggerStartMode sm, QString * m_d->m_mode = sm; } m_d->m_debuggerManagerAccess->notifyInferiorRunning(); + return true; } @@ -522,7 +539,7 @@ void CdbDebugEngine::exitDebugger() m_d->interruptInterferiorProcess(&errorMessage); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); } - hr = m_d->m_pDebugClient->DetachCurrentProcess(); + hr = m_d->m_cif.debugClient->DetachCurrentProcess(); if (FAILED(hr)) errorMessage += msgComFailed("DetachCurrentProcess", hr); if (debugCDB) @@ -536,11 +553,11 @@ void CdbDebugEngine::exitDebugger() QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); } // Terminate and wait for stop events. - hr = m_d->m_pDebugClient->TerminateCurrentProcess(); + hr = m_d->m_cif.debugClient->TerminateCurrentProcess(); if (FAILED(hr)) errorMessage += msgComFailed("TerminateCurrentProcess", hr); if (!wasRunning) { - hr = m_d->m_pDebugClient->TerminateProcesses(); + hr = m_d->m_cif.debugClient->TerminateProcesses(); if (FAILED(hr)) errorMessage += msgComFailed("TerminateProcesses", hr); } @@ -633,7 +650,7 @@ void CdbDebugEngine::filterEvaluateWatchers(QList<WatchData> *wd, WatchHandler * return; // Filter out actual watchers. Ignore the "<Edit>" top level place holders - SyntaxSetter syntaxSetter(m_d->m_pDebugControl, DEBUG_EXPR_CPLUSPLUS); + SyntaxSetter syntaxSetter(m_d->m_cif.debugControl, DEBUG_EXPR_CPLUSPLUS); const QString watcherPrefix = QLatin1String("watch."); const QChar lessThan = QLatin1Char('<'); const QChar greaterThan = QLatin1Char('>'); @@ -701,7 +718,7 @@ void CdbDebugEngine::stepExec() qDebug() << Q_FUNC_INFO; m_d->clearForRun(); - const HRESULT hr = m_d->m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_STEP_INTO); + const HRESULT hr = m_d->m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_STEP_INTO); Q_UNUSED(hr) m_d->m_breakEventMode = CdbDebugEnginePrivate::BreakEventIgnoreOnce; @@ -733,7 +750,7 @@ void CdbDebugEngine::stepOutExec() } IDebugBreakpoint2* pBP; - HRESULT hr = m_d->m_pDebugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &pBP); + HRESULT hr = m_d->m_cif.debugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &pBP); if (FAILED(hr) || !pBP) { errorMessage = QString::fromLatin1("Cannot create temporary breakpoint: %1").arg(msgDebugEngineComResult(hr)); break; @@ -756,7 +773,7 @@ void CdbDebugEngine::nextExec() qDebug() << Q_FUNC_INFO; m_d->clearForRun(); - const HRESULT hr = m_d->m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_STEP_OVER); + const HRESULT hr = m_d->m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_STEP_OVER); if (SUCCEEDED(hr)) { startWatchTimer(); } else { @@ -775,7 +792,7 @@ void CdbDebugEngine::nextIExec() qDebug() << Q_FUNC_INFO; m_d->clearForRun(); - const HRESULT hr = m_d->m_pDebugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, "p", 0); + const HRESULT hr = m_d->m_cif.debugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, "p", 0); if (SUCCEEDED(hr)) { startWatchTimer(); } else { @@ -795,7 +812,7 @@ bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessage) { if (debugCDB) qDebug() << Q_FUNC_INFO; - const HRESULT hr = m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_GO); + const HRESULT hr = m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_GO); if (FAILED(hr)) { *errorMessage = msgComFailed("SetExecutionStatus", hr); return false; @@ -807,7 +824,7 @@ bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessage) bool CdbDebugEnginePrivate::continueInferior(QString *errorMessage) { ULONG executionStatus; - if (!getExecutionStatus(m_pDebugControl, &executionStatus, errorMessage)) + if (!getExecutionStatus(m_cif.debugControl, &executionStatus, errorMessage)) return false; if (debugCDB) @@ -836,7 +853,7 @@ bool CdbDebugEnginePrivate::interruptInterferiorProcess(QString *errorMessage) // Interrupt the interferior process without notifications if (debugCDB) { ULONG executionStatus; - getExecutionStatus(m_pDebugControl, &executionStatus, errorMessage); + getExecutionStatus(m_cif.debugControl, &executionStatus, errorMessage); qDebug() << Q_FUNC_INFO << "\n ex=" << executionStatus; } @@ -845,10 +862,10 @@ bool CdbDebugEnginePrivate::interruptInterferiorProcess(QString *errorMessage) return false; } #if 0 - const HRESULT hr = m_pDebugControl->SetInterrupt(DEBUG_INTERRUPT_ACTIVE|DEBUG_INTERRUPT_EXIT); + const HRESULT hr = m_cif.debugControl->SetInterrupt(DEBUG_INTERRUPT_ACTIVE|DEBUG_INTERRUPT_EXIT); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Unable to interrupt debuggee after %1s: %2"). - arg(getInterruptTimeOutSecs(m_pDebugControl)).arg(msgComFailed("SetInterrupt", hr)); + arg(getInterruptTimeOutSecs(m_cif.debugControl)).arg(msgComFailed("SetInterrupt", hr)); return false; } #endif @@ -918,15 +935,16 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v void CdbDebugEngine::executeDebuggerCommand(const QString &command) { QString errorMessage; - if (!executeDebuggerCommand(command, &errorMessage)) + if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_d->m_cif.debugControl, command, &errorMessage)) qWarning("%s\n", qPrintable(errorMessage)); } -bool CdbDebugEngine::executeDebuggerCommand(const QString &command, QString *errorMessage) +bool CdbDebugEnginePrivate::executeDebuggerCommand(IDebugControl4 *ctrl, const QString &command, QString *errorMessage) { if (debugCDB) qDebug() << Q_FUNC_INFO << command; - const HRESULT hr = m_d->m_pDebugControl->ExecuteWide(DEBUG_OUTCTL_THIS_CLIENT, command.utf16(), 0); + // output to all clients, else we do not see anything + const HRESULT hr = ctrl->ExecuteWide(DEBUG_OUTCTL_ALL_CLIENTS, command.utf16(), 0); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Unable to execute '%1': %2"). arg(command, msgDebugEngineComResult(hr)); @@ -945,9 +963,9 @@ bool CdbDebugEngine::evaluateExpression(const QString &expression, DEBUG_VALUE debugValue; memset(&debugValue, 0, sizeof(DEBUG_VALUE)); // Original syntax must be restored, else setting breakpoints will fail. - SyntaxSetter syntaxSetter(m_d->m_pDebugControl, DEBUG_EXPR_CPLUSPLUS); + SyntaxSetter syntaxSetter(m_d->m_cif.debugControl, DEBUG_EXPR_CPLUSPLUS); ULONG errorPosition = 0; - const HRESULT hr = m_d->m_pDebugControl->EvaluateWide(expression.utf16(), + const HRESULT hr = m_d->m_cif.debugControl->EvaluateWide(expression.utf16(), DEBUG_VALUE_INVALID, &debugValue, &errorPosition); if (FAILED(hr)) { if (HRESULT_CODE(hr) == 517) { @@ -959,7 +977,7 @@ bool CdbDebugEngine::evaluateExpression(const QString &expression, } return false; } - *value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_pDebugControl, type); + *value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_cif.debugControl, type); return true; } @@ -1044,7 +1062,7 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa // called again from the debug event handler. ULONG dummy; - const bool wasRunning = !CDBBreakPoint::getBreakPointCount(m_pDebugControl, &dummy); + const bool wasRunning = !CDBBreakPoint::getBreakPointCount(m_cif.debugControl, &dummy); if (debugCDB) qDebug() << Q_FUNC_INFO << "\n Running=" << wasRunning; @@ -1058,8 +1076,8 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa return true; } - return CDBBreakPoint::synchronizeBreakPoints(m_pDebugControl, - m_pDebugSymbols, + return CDBBreakPoint::synchronizeBreakPoints(m_cif.debugControl, + m_cif.debugSymbols, m_debuggerManagerAccess->breakHandler(), errorMessage); } @@ -1085,7 +1103,7 @@ void CdbDebugEngine::reloadDisassembler() QList<DisassemblerLine> lines; QString errorMessage; QApplication::setOverrideCursor(Qt::WaitCursor); - const bool drc = dissassemble(m_d->m_pDebugClient, m_d->m_pDebugControl, offset, + const bool drc = dissassemble(m_d->m_cif.debugClient, m_d->m_cif.debugControl, offset, ContextLines, ContextLines, &lines, &errorMessage); QApplication::restoreOverrideCursor(); if (drc) { @@ -1126,7 +1144,7 @@ QList<Symbol> CdbDebugEngine::moduleSymbols(const QString &moduleName) errorMessage = tr("Cannot retrieve symbols while the debuggee is running."); break; } - if (!getModuleSymbols(m_d->m_pDebugSymbols, moduleName, &rc, &errorMessage)) + if (!getModuleSymbols(m_d->m_cif.debugSymbols, moduleName, &rc, &errorMessage)) break; success = true; } while (false); @@ -1161,7 +1179,7 @@ void CdbDebugEngine::reloadRegisters() qDebug() << Q_FUNC_INFO << intBase; QList<Register> registers; QString errorMessage; - if (!getRegisters(m_d->m_pDebugControl, m_d->m_pDebugRegisters, ®isters, &errorMessage, intBase)) + if (!getRegisters(m_d->m_cif.debugControl, m_d->m_cif.debugRegisters, ®isters, &errorMessage, intBase)) qWarning("reloadRegisters() failed: %s\n", qPrintable(errorMessage)); m_d->m_debuggerManagerAccess->registerHandler()->setRegisters(registers); } @@ -1171,7 +1189,7 @@ void CdbDebugEngine::timerEvent(QTimerEvent* te) if (te->timerId() != m_d->m_watchTimer) return; - const HRESULT hr = m_d->m_pDebugControl->WaitForEvent(0, 1); + const HRESULT hr = m_d->m_cif.debugControl->WaitForEvent(0, 1); if (debugCDB) if (debugCDB > 1 || hr != S_FALSE) qDebug() << Q_FUNC_INFO << "WaitForEvent" << m_d->m_debuggerManager->status() << msgDebugEngineComResult(hr); @@ -1266,7 +1284,7 @@ void CdbDebugEnginePrivate::updateThreadList() QString errorMessage; do { ULONG numberOfThreads; - HRESULT hr= m_pDebugSystemObjects->GetNumberThreads(&numberOfThreads); + HRESULT hr= m_cif.debugSystemObjects->GetNumberThreads(&numberOfThreads); if (FAILED(hr)) { errorMessage= msgComFailed("GetNumberThreads", hr); break; @@ -1274,7 +1292,7 @@ void CdbDebugEnginePrivate::updateThreadList() const ULONG maxThreadIds = 256; ULONG threadIds[maxThreadIds]; ULONG biggestThreadId = qMin(maxThreadIds, numberOfThreads - 1); - hr = m_pDebugSystemObjects->GetThreadIdsByIndex(0, biggestThreadId, threadIds, 0); + hr = m_cif.debugSystemObjects->GetThreadIdsByIndex(0, biggestThreadId, threadIds, 0); if (FAILED(hr)) { errorMessage= msgComFailed("GetThreadIdsByIndex", hr); break; @@ -1301,8 +1319,8 @@ void CdbDebugEnginePrivate::updateStackTrace() QString errorMessage; m_engine->reloadRegisters(); m_currentStackTrace = - CdbStackTraceContext::create(m_pDebugControl, m_pDebugSystemObjects, - m_pDebugSymbols, m_currentThreadId, &errorMessage); + CdbStackTraceContext::create(m_cif.debugControl, m_cif.debugSystemObjects, + m_cif.debugSymbols, m_currentThreadId, &errorMessage); if (!m_currentStackTrace) { qWarning("%s: failed to create trace context: %s", Q_FUNC_INFO, qPrintable(errorMessage)); return; @@ -1329,17 +1347,36 @@ void CdbDebugEnginePrivate::updateStackTrace() } } - void CdbDebugEnginePrivate::updateModules() { QList<Module> modules; QString errorMessage; - if (!getModuleList(m_pDebugSymbols, &modules, &errorMessage)) + if (!getModuleList(m_cif.debugSymbols, &modules, &errorMessage)) qWarning("updateModules() failed: %s\n", qPrintable(errorMessage)); m_debuggerManagerAccess->modulesHandler()->setModules(modules); } -void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT pBP) +static const char *dumperPrefixC = "dumper"; + +void CdbDebugEnginePrivate::handleModuleLoad(const QString &name) +{ + if (debugCDB>2) + qDebug() << Q_FUNC_INFO << "\n " << name; + 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) { Q_UNUSED(pBP) if (debugCDB) diff --git a/src/plugins/debugger/cdb/cdbdebugengine_p.h b/src/plugins/debugger/cdb/cdbdebugengine_p.h index ae4812d7d8183cb6a0520240aee50a926c4a1502..88cbb2725c9b5b6661acec727f089bb20fa7ea2e 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine_p.h +++ b/src/plugins/debugger/cdb/cdbdebugengine_p.h @@ -33,6 +33,7 @@ #include "cdbdebugeventcallback.h" #include "cdbdebugoutput.h" #include "cdboptions.h" +#include "cdbdumperhelper.h" #include "stackhandler.h" #include "debuggermanager.h" @@ -67,6 +68,31 @@ private: DebugCreateFunction m_debugCreate; }; +// A class that sets an expression syntax on the debug control while in scope. +// Can be nested as it checks for the old value. +class SyntaxSetter { + Q_DISABLE_COPY(SyntaxSetter) +public: + explicit inline SyntaxSetter(IDebugControl4 *ctl, ULONG desiredSyntax); + inline ~SyntaxSetter(); +private: + const ULONG m_desiredSyntax; + IDebugControl4 *m_ctl; + ULONG m_oldSyntax; +}; + +// helper struct to pass interfaces around +struct CdbComInterfaces +{ + CdbComInterfaces(); + IDebugClient5* debugClient; + IDebugControl4* debugControl; + IDebugSystemObjects4* debugSystemObjects; + IDebugSymbols3* debugSymbols; + IDebugRegisters2* debugRegisters; + IDebugDataSpaces4* debugDataSpaces; +}; + struct CdbDebugEnginePrivate { enum HandleBreakEventMode { // Special modes for break event handler. @@ -90,9 +116,10 @@ struct CdbDebugEnginePrivate bool updateLocals(int frameIndex, WatchHandler *wh, QString *errorMessage); void updateModules(); - void handleBreakpointEvent(PDEBUG_BREAKPOINT pBP); + void handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP); void cleanStackTrace(); void clearForRun(); + void handleModuleLoad(const QString &); CdbSymbolGroupContext *getStackFrameSymbolGroupContext(int frameIndex, QString *errorMessage) const; void clearDisplay(); @@ -103,6 +130,8 @@ struct CdbDebugEnginePrivate bool attemptBreakpointSynchronization(QString *errorMessage); + static bool executeDebuggerCommand(IDebugControl4 *ctrl, const QString &command, QString *errorMessage); + const QSharedPointer<CdbOptions> m_options; HANDLE m_hDebuggeeProcess; HANDLE m_hDebuggeeThread; @@ -110,13 +139,10 @@ struct CdbDebugEnginePrivate HandleBreakEventMode m_breakEventMode; int m_watchTimer; - IDebugClient5* m_pDebugClient; - IDebugControl4* m_pDebugControl; - IDebugSystemObjects4* m_pDebugSystemObjects; - IDebugSymbols3* m_pDebugSymbols; - IDebugRegisters2* m_pDebugRegisters; + CdbComInterfaces m_cif; CdbDebugEventCallback m_debugEventCallBack; - CdbDebugOutput m_debugOutputCallBack; + CdbDebugOutput m_debugOutputCallBack; + CdbDumperHelper m_dumper; CdbDebugEngine* m_engine; DebuggerManager *m_debuggerManager; diff --git a/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp b/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp index 526902e2c3d8e3603925178335c648c082b33d89..2e6b2178f19ca681443fee4fe278ee6032171f4a 100644 --- a/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp +++ b/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp @@ -38,12 +38,12 @@ namespace Debugger { namespace Internal { -CdbDebugEventCallback::CdbDebugEventCallback(CdbDebugEngine* dbg) : - m_pEngine(dbg) +// CdbDebugEventCallbackBase +CdbDebugEventCallbackBase::CdbDebugEventCallbackBase() { } -STDMETHODIMP CdbDebugEventCallback::QueryInterface( +STDMETHODIMP CdbDebugEventCallbackBase::QueryInterface( THIS_ IN REFIID InterfaceId, OUT PVOID* Interface) @@ -60,32 +60,170 @@ STDMETHODIMP CdbDebugEventCallback::QueryInterface( } } -STDMETHODIMP_(ULONG) CdbDebugEventCallback::AddRef(THIS) +STDMETHODIMP_(ULONG) CdbDebugEventCallbackBase::AddRef(THIS) { // This class is designed to be static so // there's no true refcount. return 1; } -STDMETHODIMP_(ULONG) CdbDebugEventCallback::Release(THIS) +STDMETHODIMP_(ULONG) CdbDebugEventCallbackBase::Release(THIS) { // This class is designed to be static so // there's no true refcount. return 0; } +STDMETHODIMP CdbDebugEventCallbackBase::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT2) +{ + return S_OK; +} +STDMETHODIMP CdbDebugEventCallbackBase::Exception( + THIS_ + __in PEXCEPTION_RECORD64, + __in ULONG /* FirstChance */ + ) +{ + return S_OK; +} + +STDMETHODIMP CdbDebugEventCallbackBase::CreateThread( + THIS_ + __in ULONG64 /* Handle */, + __in ULONG64 /* DataOffset */, + __in ULONG64 /* StartOffset */ + ) +{ + return S_OK; +} + +STDMETHODIMP CdbDebugEventCallbackBase::ExitThread( + THIS_ + __in ULONG /* ExitCode */ + ) +{ + return S_OK; +} + +STDMETHODIMP CdbDebugEventCallbackBase::CreateProcess( + THIS_ + __in ULONG64 /* ImageFileHandle */, + __in ULONG64 /* Handle */, + __in ULONG64 /* BaseOffset */, + __in ULONG /* ModuleSize */, + __in_opt PCWSTR /* ModuleName */, + __in_opt PCWSTR /* ImageName */, + __in ULONG /* CheckSum */, + __in ULONG /* TimeDateStamp */, + __in ULONG64 /* InitialThreadHandle */, + __in ULONG64 /* ThreadDataOffset */, + __in ULONG64 /* StartOffset */ + ) +{ + return S_OK; +} + +STDMETHODIMP CdbDebugEventCallbackBase::ExitProcess( + THIS_ + __in ULONG /* ExitCode */ + ) +{ + return S_OK; +} + +STDMETHODIMP CdbDebugEventCallbackBase::LoadModule( + THIS_ + __in ULONG64 /* ImageFileHandle */, + __in ULONG64 /* BaseOffset */, + __in ULONG /* ModuleSize */, + __in_opt PCWSTR /* ModuleName */, + __in_opt PCWSTR /* ImageName */, + __in ULONG /* CheckSum */, + __in ULONG /* TimeDateStamp */ + ) +{ + return S_OK; +} + +STDMETHODIMP CdbDebugEventCallbackBase::UnloadModule( + THIS_ + __in_opt PCWSTR /* ImageBaseName */, + __in ULONG64 /* BaseOffset */ + ) +{ + return S_OK; +} + +STDMETHODIMP CdbDebugEventCallbackBase::SystemError( + THIS_ + __in ULONG /* Error */, + __in ULONG /* Level */ + ) +{ + return S_OK; +} + +STDMETHODIMP CdbDebugEventCallbackBase::SessionStatus( + THIS_ + __in ULONG /* Status */ + ) +{ + return S_OK; +} + +STDMETHODIMP CdbDebugEventCallbackBase::ChangeDebuggeeState( + THIS_ + __in ULONG /* Flags */, + __in ULONG64 /* Argument */ + ) +{ + return S_OK; +} + +STDMETHODIMP CdbDebugEventCallbackBase::ChangeEngineState( + THIS_ + __in ULONG /* Flags */, + __in ULONG64 /* Argument */ + ) +{ + return S_OK; +} + +STDMETHODIMP CdbDebugEventCallbackBase::ChangeSymbolState( + THIS_ + __in ULONG /* Flags */, + __in ULONG64 /* Argument */ + ) +{ + return S_OK; +} + +IDebugEventCallbacksWide *CdbDebugEventCallbackBase::getEventCallback(IDebugClient5 *clnt) +{ + IDebugEventCallbacksWide *rc = 0; + if (SUCCEEDED(clnt->GetEventCallbacksWide(&rc))) + return rc; + return 0; +} + +// ---------- CdbDebugEventCallback + +CdbDebugEventCallback::CdbDebugEventCallback(CdbDebugEngine* dbg) : + m_pEngine(dbg) +{ +} + STDMETHODIMP CdbDebugEventCallback::GetInterestMask(THIS_ __out PULONG mask) { *mask = DEBUG_EVENT_CREATE_PROCESS | DEBUG_EVENT_EXIT_PROCESS | DEBUG_EVENT_LOAD_MODULE | DEBUG_EVENT_UNLOAD_MODULE | DEBUG_EVENT_CREATE_THREAD | DEBUG_EVENT_EXIT_THREAD | DEBUG_EVENT_BREAKPOINT - | DEBUG_EVENT_EXCEPTION - ; + | DEBUG_EVENT_EXCEPTION; return S_OK; } -STDMETHODIMP CdbDebugEventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT Bp) +STDMETHODIMP CdbDebugEventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT2 Bp) { if (debugCDB) qDebug() << Q_FUNC_INFO; @@ -150,8 +288,8 @@ STDMETHODIMP CdbDebugEventCallback::CreateProcess( __in ULONG64 Handle, __in ULONG64 BaseOffset, __in ULONG ModuleSize, - __in_opt PCSTR ModuleName, - __in_opt PCSTR ImageName, + __in_opt PCWSTR ModuleName, + __in_opt PCWSTR ImageName, __in ULONG CheckSum, __in ULONG TimeDateStamp, __in ULONG64 InitialThreadHandle, @@ -175,7 +313,7 @@ STDMETHODIMP CdbDebugEventCallback::CreateProcess( m_pEngine->m_d->m_debuggerManagerAccess->notifyInferiorRunning(); ULONG currentThreadId; - if (SUCCEEDED(m_pEngine->m_d->m_pDebugSystemObjects->GetThreadIdByHandle(InitialThreadHandle, ¤tThreadId))) + 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; @@ -201,8 +339,8 @@ STDMETHODIMP CdbDebugEventCallback::LoadModule( __in ULONG64 ImageFileHandle, __in ULONG64 BaseOffset, __in ULONG ModuleSize, - __in_opt PCSTR ModuleName, - __in_opt PCSTR ImageName, + __in_opt PCWSTR ModuleName, + __in_opt PCWSTR ImageName, __in ULONG CheckSum, __in ULONG TimeDateStamp ) @@ -216,13 +354,13 @@ STDMETHODIMP CdbDebugEventCallback::LoadModule( Q_UNUSED(TimeDateStamp) if (debugCDB > 1) qDebug() << Q_FUNC_INFO << ModuleName; - m_pEngine->m_d->updateModules(); + m_pEngine->m_d->handleModuleLoad(QString::fromUtf16(ModuleName)); return S_OK; } STDMETHODIMP CdbDebugEventCallback::UnloadModule( THIS_ - __in_opt PCSTR ImageBaseName, + __in_opt PCWSTR ImageBaseName, __in ULONG64 BaseOffset ) { @@ -245,46 +383,28 @@ STDMETHODIMP CdbDebugEventCallback::SystemError( return S_OK; } -STDMETHODIMP CdbDebugEventCallback::SessionStatus( - THIS_ - __in ULONG Status - ) +// -----------IgnoreDebugEventCallback +IgnoreDebugEventCallback::IgnoreDebugEventCallback() { - Q_UNUSED(Status) - return S_OK; } -STDMETHODIMP CdbDebugEventCallback::ChangeDebuggeeState( - THIS_ - __in ULONG Flags, - __in ULONG64 Argument - ) +STDMETHODIMP IgnoreDebugEventCallback::GetInterestMask(THIS_ __out PULONG mask) { - Q_UNUSED(Flags) - Q_UNUSED(Argument) + *mask = 0; return S_OK; } -STDMETHODIMP CdbDebugEventCallback::ChangeEngineState( - THIS_ - __in ULONG Flags, - __in ULONG64 Argument - ) +// --------- EventCallbackRedirector +EventCallbackRedirector::EventCallbackRedirector(IDebugClient5 *client, IDebugEventCallbacksWide *cb) : + m_client(client), + m_oldCb(CdbDebugEventCallbackBase::getEventCallback(client)) { - Q_UNUSED(Flags) - Q_UNUSED(Argument) - return S_OK; + client->SetEventCallbacksWide(cb); } -STDMETHODIMP CdbDebugEventCallback::ChangeSymbolState( - THIS_ - __in ULONG Flags, - __in ULONG64 Argument - ) +EventCallbackRedirector::~EventCallbackRedirector() { - Q_UNUSED(Flags) - Q_UNUSED(Argument) - return S_OK; + m_client->SetEventCallbacksWide(m_oldCb); } } // namespace Internal diff --git a/src/plugins/debugger/cdb/cdbdebugeventcallback.h b/src/plugins/debugger/cdb/cdbdebugeventcallback.h index d3ab4974f64c8cf497cb9a38d269f1982c6eee5c..d33aa4375547b89d8b3709dfe9733e58fc5e1032 100644 --- a/src/plugins/debugger/cdb/cdbdebugeventcallback.h +++ b/src/plugins/debugger/cdb/cdbdebugeventcallback.h @@ -32,17 +32,21 @@ #include <windows.h> #include <inc/dbgeng.h> +#include <QtCore/QtGlobal> namespace Debugger { namespace Internal { class CdbDebugEngine; -class CdbDebugEventCallback : public IDebugEventCallbacks +// Base class for event callbacks that takes care +// Active X magic. Provides base implementations with +// the exception of GetInterestMask +class CdbDebugEventCallbackBase : public IDebugEventCallbacksWide { +protected: + CdbDebugEventCallbackBase(); public: - explicit CdbDebugEventCallback(CdbDebugEngine* dbg); - // IUnknown. STDMETHOD(QueryInterface)( THIS_ @@ -58,14 +62,9 @@ public: // IDebugEventCallbacks. - STDMETHOD(GetInterestMask)( - THIS_ - __out PULONG mask - ); - STDMETHOD(Breakpoint)( THIS_ - __in PDEBUG_BREAKPOINT Bp + __in PDEBUG_BREAKPOINT2 Bp ); STDMETHOD(Exception)( @@ -91,8 +90,8 @@ public: __in ULONG64 Handle, __in ULONG64 BaseOffset, __in ULONG ModuleSize, - __in_opt PCSTR ModuleName, - __in_opt PCSTR ImageName, + __in_opt PCWSTR ModuleName, + __in_opt PCWSTR ImageName, __in ULONG CheckSum, __in ULONG TimeDateStamp, __in ULONG64 InitialThreadHandle, @@ -110,15 +109,15 @@ public: __in ULONG64 ImageFileHandle, __in ULONG64 BaseOffset, __in ULONG ModuleSize, - __in_opt PCSTR ModuleName, - __in_opt PCSTR ImageName, + __in_opt PCWSTR ModuleName, + __in_opt PCWSTR ImageName, __in ULONG CheckSum, __in ULONG TimeDateStamp ); STDMETHOD(UnloadModule)( THIS_ - __in_opt PCSTR ImageBaseName, + __in_opt PCWSTR ImageBaseName, __in ULONG64 BaseOffset ); @@ -151,10 +150,116 @@ public: __in ULONG64 Argument ); + + static IDebugEventCallbacksWide *getEventCallback(IDebugClient5 *clnt); +}; + +class CdbDebugEventCallback : public CdbDebugEventCallbackBase +{ +public: + explicit CdbDebugEventCallback(CdbDebugEngine* dbg); + + // IDebugEventCallbacks. + + STDMETHOD(GetInterestMask)( + THIS_ + __out PULONG mask + ); + + STDMETHOD(Breakpoint)( + THIS_ + __in PDEBUG_BREAKPOINT2 Bp + ); + + STDMETHOD(Exception)( + THIS_ + __in PEXCEPTION_RECORD64 Exception, + __in ULONG FirstChance + ); + + STDMETHOD(CreateThread)( + THIS_ + __in ULONG64 Handle, + __in ULONG64 DataOffset, + __in ULONG64 StartOffset + ); + STDMETHOD(ExitThread)( + THIS_ + __in ULONG ExitCode + ); + + STDMETHOD(CreateProcess)( + THIS_ + __in ULONG64 ImageFileHandle, + __in ULONG64 Handle, + __in ULONG64 BaseOffset, + __in ULONG ModuleSize, + __in_opt PCWSTR ModuleName, + __in_opt PCWSTR ImageName, + __in ULONG CheckSum, + __in ULONG TimeDateStamp, + __in ULONG64 InitialThreadHandle, + __in ULONG64 ThreadDataOffset, + __in ULONG64 StartOffset + ); + + STDMETHOD(ExitProcess)( + THIS_ + __in ULONG ExitCode + ); + + STDMETHOD(LoadModule)( + THIS_ + __in ULONG64 ImageFileHandle, + __in ULONG64 BaseOffset, + __in ULONG ModuleSize, + __in_opt PCWSTR ModuleName, + __in_opt PCWSTR ImageName, + __in ULONG CheckSum, + __in ULONG TimeDateStamp + ); + + STDMETHOD(UnloadModule)( + THIS_ + __in_opt PCWSTR ImageBaseName, + __in ULONG64 BaseOffset + ); + + STDMETHOD(SystemError)( + THIS_ + __in ULONG Error, + __in ULONG Level + ); + private: CdbDebugEngine *m_pEngine; }; +// Event handler that ignores everything +class IgnoreDebugEventCallback : public CdbDebugEventCallbackBase +{ +public: + explicit IgnoreDebugEventCallback(); + + STDMETHOD(GetInterestMask)( + THIS_ + __out PULONG mask + ); +}; + +// Utility class to temporarily redirect events to another handler +// as long as in scope +class EventCallbackRedirector { + Q_DISABLE_COPY(EventCallbackRedirector) +public: + explicit EventCallbackRedirector(IDebugClient5 *client, IDebugEventCallbacksWide *cb); + ~EventCallbackRedirector(); +private: + IDebugClient5 *m_client; + IDebugEventCallbacksWide *m_oldCb; +}; + + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbdebugoutput.cpp b/src/plugins/debugger/cdb/cdbdebugoutput.cpp index 114c59c7712b31733d3ae99f6e85c6434e8ef1a4..8517b0f220bc901b311f07b0f0bc7de1694c651e 100644 --- a/src/plugins/debugger/cdb/cdbdebugoutput.cpp +++ b/src/plugins/debugger/cdb/cdbdebugoutput.cpp @@ -155,5 +155,18 @@ void CdbDebugOutput::output(ULONG mask, const QString &msg) } } +// Utility class to temporarily redirect output to another handler +// as long as in scope +OutputRedirector::OutputRedirector(IDebugClient5 *client, IDebugOutputCallbacksWide *newHandler) : + m_client(client), + m_oldHandler(CdbDebugOutputBase::getOutputCallback(client)) +{ + m_client->SetOutputCallbacksWide(newHandler); +} +OutputRedirector::~OutputRedirector() +{ + m_client->SetOutputCallbacksWide(m_oldHandler); +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbdebugoutput.h b/src/plugins/debugger/cdb/cdbdebugoutput.h index f13fedac35e57f1fc17444361daffeef0966ad11..9deb527e9a39fff970cd67297ad85768263fce67 100644 --- a/src/plugins/debugger/cdb/cdbdebugoutput.h +++ b/src/plugins/debugger/cdb/cdbdebugoutput.h @@ -105,6 +105,18 @@ private: QString m_result; }; +// Utility class to temporarily redirect output to another handler +// as long as in scope +class OutputRedirector { + Q_DISABLE_COPY(OutputRedirector) +public: + explicit OutputRedirector(IDebugClient5 *client, IDebugOutputCallbacksWide *newHandler); + ~OutputRedirector(); +private: + IDebugClient5 *m_client; + IDebugOutputCallbacksWide *m_oldHandler; +}; + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.cpp b/src/plugins/debugger/cdb/cdbdumperhelper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d0b4332811bb641ebe4ef3d3e7d72d4928ead295 --- /dev/null +++ b/src/plugins/debugger/cdb/cdbdumperhelper.cpp @@ -0,0 +1,223 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +**************************************************************************/ + +#include "cdbdumperhelper.h" +#include "cdbmodules.h" +#include "cdbdebugengine_p.h" +#include "cdbdebugoutput.h" +#include "cdbdebugeventcallback.h" + +#include <QtCore/QRegExp> + +enum { loadDebug = 0 }; + +namespace Debugger { +namespace Internal { + +// Alloc memory in debuggee using the ".dvalloc" command as +// there seems to be no API for it. +static bool allocDebuggeeMemory(IDebugControl4 *ctl, + IDebugClient5 *client, + int size, ULONG64 *address, QString *errorMessage) +{ + *address = 0; + const QString allocCmd = QLatin1String(".dvalloc ") + QString::number(size); + StringOutputHandler stringHandler; + OutputRedirector redir(client, &stringHandler); + if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, allocCmd, errorMessage)) + return false; + // "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized + bool ok = false; + const QString output = stringHandler.result(); + const int lastBlank = output.lastIndexOf(QLatin1Char(' ')); + if (lastBlank != -1) { + const qint64 addri = output.mid(lastBlank + 1).toLongLong(&ok, 16); + if (ok) + *address = addri; + } + if (!ok) { + *errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output); + return false; + } + return true; +} + +// Alloc an AscII string in debuggee +static bool createDebuggeeAscIIString(IDebugControl4 *ctl, + IDebugClient5 *client, + IDebugDataSpaces4 *data, + const QString &s, + ULONG64 *address, + QString *errorMessage) +{ + QByteArray sAsciiData = s.toLocal8Bit(); + sAsciiData += '\0'; + if (!allocDebuggeeMemory(ctl, client, sAsciiData.size(), address, errorMessage)) + return false; + const HRESULT hr = data->WriteVirtual(*address, sAsciiData.data(), sAsciiData.size(), 0); + if (FAILED(hr)) { + *errorMessage= msgComFailed("WriteVirtual", hr); + return false; + } + 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(IDebugSymbols3 *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 +// the QtCored4.pdb file to be present as we need "qstrdup" +// as dummy symbol. This is ok ATM since dumpers only +// make sense for Qt apps. +static bool debuggeeLoadLibrary(IDebugControl4 *ctl, + IDebugClient5 *client, + IDebugSymbols3 *syms, + IDebugDataSpaces4 *data, + const QString &moduleName, QString *errorMessage) +{ + if (loadDebug) + qDebug() << Q_FUNC_INFO << moduleName; + // Try to ignore the breakpoints + IgnoreDebugEventCallback devNull; + EventCallbackRedirector eventRedir(client, &devNull); + // Make a call to LoadLibraryA. First, reserve memory in debugger + // and copy name over. + ULONG64 nameAddress; + if (!createDebuggeeAscIIString(ctl, client, data, moduleName, &nameAddress, errorMessage)) + return false; + // We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)" + // (void* LoadLibraryA(char*)). However, despite providing a symbol + // server, the debugger refuses to recognize it as a function. + // Set up the call stack with a function of same signature (qstrdup) + // and change the call register to LoadLibraryA() before executing "g". + // Prepare call. + const QString dummyFunc = resolveStrdup(syms, errorMessage); + if (dummyFunc.isEmpty()) + return false; + QString callCmd = QLatin1String(".call "); + callCmd += dummyFunc; + callCmd += QLatin1String("(0x"); + callCmd += QString::number(nameAddress, 16); + callCmd += QLatin1Char(')'); + if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, callCmd, errorMessage)) + return false; + if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QLatin1String("r eip=Kernel32!LoadLibraryA"), errorMessage)) + return false; + // This will hit a breakpoint + if (loadDebug) + qDebug() << " executing 'g'"; + if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QString(QLatin1Char('g')), errorMessage)) + return false; + // @Todo: We cannot evaluate output here as it is asynchronous + return true; +} + +CdbDumperHelper::CdbDumperHelper(CdbComInterfaces *cif) : + m_state(NotLoaded), + m_cif(cif) +{ +} + +void CdbDumperHelper::reset(const QString &library, bool enabled) +{ + m_library = library; + m_state = enabled ? NotLoaded : Disabled; + m_dumpObjectSymbol = QLatin1String("qDumpObjectData440"); + m_errorMessage.clear(); +} + +bool CdbDumperHelper::moduleLoadHook(const QString &name, bool *ignoreNextBreakPoint) +{ + *ignoreNextBreakPoint = false; + bool ok = true; // report failure only once + switch (m_state) { + case Disabled: + break; + case NotLoaded: + // Load once QtCore is there. + if (name.contains(QLatin1String("QtCore"))) { + if (loadDebug) + qDebug() << Q_FUNC_INFO << '\n' << name << m_state; + ok = debuggeeLoadLibrary(m_cif->debugControl, m_cif->debugClient, m_cif->debugSymbols, m_cif->debugDataSpaces, + m_library, &m_errorMessage); + if (ok) { + m_state = Loading; + *ignoreNextBreakPoint = true; + } else { + m_state = Failed; + } + } + break; + case Loading: + // Hurray, loaded. Now resolve the symbols we need + if (name.contains(QLatin1String("gdbmacros"))) { + ok = resolveSymbols(&m_errorMessage); + if (ok) { + m_state = Loaded; + } else { + m_state = Failed; + } + } + break; + case Loaded: + break; + case Failed: + break; + }; + return ok; +} + +bool CdbDumperHelper::resolveSymbols(QString *errorMessage) +{ + // Resolve the symbols we need + m_dumpObjectSymbol = QLatin1String("qDumpObjectData440"); + const bool rc = resolveSymbol(m_cif->debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk; + if (loadDebug) + qDebug() << Q_FUNC_INFO << '\n' << rc << m_dumpObjectSymbol; + return rc; +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.h b/src/plugins/debugger/cdb/cdbdumperhelper.h new file mode 100644 index 0000000000000000000000000000000000000000..8947acd134cc344e347c989118ea8f53a6ccc69c --- /dev/null +++ b/src/plugins/debugger/cdb/cdbdumperhelper.h @@ -0,0 +1,87 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +**************************************************************************/ + +#ifndef CDBDUMPERHELPER_H +#define CDBDUMPERHELPER_H + +#include <QtCore/QString> + +namespace Debugger { +namespace Internal { + +struct CdbComInterfaces; + +// For code clarity, all the stuff related to custom dumpers +// goes here. +// "Custom dumper" is a library compiled against the current +// Qt containing functions to evaluate values of Qt classes +// (such as QString, taking pointers to their addresses). +// The library must be loaded into the debuggee. + +class CdbDumperHelper +{ +public: + enum State { + Disabled, + NotLoaded, + Loading, + Loaded, + Failed + }; + + explicit CdbDumperHelper(CdbComInterfaces *cif); + + // Call before starting the debugger + void reset(const QString &library, bool enabled); + + // Call from the module loaded event handler. + // It will load the dumper library and resolve the required symbols + // 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: + bool resolveSymbols(QString *errorMessage); + + State m_state; + CdbComInterfaces *m_cif; + + QString m_library; + QString m_dumpObjectSymbol; + QString m_errorMessage; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // CDBDUMPERHELPER_H diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp index 6c6bb90ea0be188cc3c2b95208822cd97b492fe0..f0c36ad222d7e763aa0bae9b3e25f2640864adbf 100644 --- a/src/plugins/debugger/debuggermanager.cpp +++ b/src/plugins/debugger/debuggermanager.cpp @@ -56,6 +56,7 @@ #ifdef Q_OS_WIN # include "peutils.h" #endif +#include <coreplugin/icore.h> #include <utils/qtcassert.h> #include <QtCore/QDebug> @@ -79,6 +80,7 @@ #include <QtGui/QTextCursor> #include <QtGui/QToolBar> #include <QtGui/QToolButton> +#include <QtGui/QPushButton> #include <QtGui/QToolTip> using namespace Debugger; @@ -1466,6 +1468,52 @@ void DebuggerManager::reloadRegisters() m_engine->reloadRegisters(); } +////////////////////////////////////////////////////////////////////// +// +// Dumpers. "Custom dumpers" are a library compiled against the current +// Qt containing functions to evaluate values of Qt classes +// (such as QString, taking pointers to their addresses). +// The library must be loaded into the debuggee. +// +////////////////////////////////////////////////////////////////////// + +bool DebuggerManager::qtDumperLibraryEnabled() const +{ + return theDebuggerBoolSetting(UseDebuggingHelpers); +} + +QString DebuggerManager::qtDumperLibraryName() const +{ + if (theDebuggerAction(UseCustomDebuggingHelperLocation)->value().toBool()) + return theDebuggerAction(CustomDebuggingHelperLocation)->value().toString(); + return m_dumperLib; +} + +void DebuggerManager::showQtDumperLibraryWarning(const QString &details) +{ + QMessageBox dialog(mainWindow()); + QPushButton *qtPref = dialog.addButton(tr("Open Qt preferences"), QMessageBox::ActionRole); + QPushButton *helperOff = dialog.addButton(tr("Turn helper usage off"), QMessageBox::ActionRole); + QPushButton *justContinue = dialog.addButton(tr("Continue anyway"), QMessageBox::AcceptRole); + dialog.setDefaultButton(justContinue); + dialog.setWindowTitle(tr("Debugging helper missing")); + dialog.setText(tr("The debugger did not find the debugging helper library.")); + dialog.setInformativeText(tr("The debugging helper is used to nicely format the values of Qt " + "data types and some STL data types. " + "It must be compiled for each Qt version, " + "you can do this in the Qt preferences page by selecting " + "a Qt installation and clicking on 'Rebuild' for the debugging " + "helper.")); + if (!details.isEmpty()) + dialog.setDetailedText(details); + dialog.exec(); + if (dialog.clickedButton() == qtPref) { + Core::ICore::instance()->showOptionsDialog(QLatin1String("Qt4"), QLatin1String("Qt Versions")); + } else if (dialog.clickedButton() == helperOff) { + theDebuggerAction(UseDebuggingHelpers)->setValue(qVariantFromValue(false), false); + } +} + ////////////////////////////////////////////////////////////////////// // diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h index 4062be169dcd5a90e921c853d32c3cec7bd78e2a..e5117845a95ae5f44198d356b67ef507e9862ade 100644 --- a/src/plugins/debugger/debuggermanager.h +++ b/src/plugins/debugger/debuggermanager.h @@ -164,6 +164,8 @@ private: virtual SourceFilesWindow *sourceFileWindow() = 0; virtual void showApplicationOutput(const QString &data) = 0; + virtual void showDebuggerOutput(const QString &prefix, const QString &msg) = 0; + virtual void showDebuggerInput(const QString &prefix, const QString &msg) = 0; virtual void reloadDisassembler() = 0; virtual void reloadModules() = 0; @@ -250,6 +252,10 @@ public slots: void showStatusMessage(const QString &msg, int timeout = -1); // -1 forever + bool qtDumperLibraryEnabled() const; + QString qtDumperLibraryName() const; + void showQtDumperLibraryWarning(const QString &details); + private slots: void showDebuggerOutput(const QString &prefix, const QString &msg); void showDebuggerInput(const QString &prefix, const QString &msg); diff --git a/src/plugins/debugger/gdbengine.cpp b/src/plugins/debugger/gdbengine.cpp index 7b08b13fbdb2362e08309c46df37ad8a6ca5fb06..feef41394e274e7ddd77744460c920119d74f591 100644 --- a/src/plugins/debugger/gdbengine.cpp +++ b/src/plugins/debugger/gdbengine.cpp @@ -4128,36 +4128,6 @@ void GdbEngine::assignValueInDebugger(const QString &expression, const QString & sendCommand("-var-assign assign " + value, WatchVarAssign); } -QString GdbEngine::dumperLibraryName() const -{ - if (theDebuggerAction(UseCustomDebuggingHelperLocation)->value().toBool()) - return theDebuggerAction(CustomDebuggingHelperLocation)->value().toString(); - return q->m_dumperLib; -} - -void GdbEngine::showDebuggingHelperWarning() -{ - QMessageBox dialog(q->mainWindow()); - QPushButton *qtPref = dialog.addButton(tr("Open Qt preferences"), QMessageBox::ActionRole); - QPushButton *helperOff = dialog.addButton(tr("Turn helper usage off"), QMessageBox::ActionRole); - QPushButton *justContinue = dialog.addButton(tr("Continue anyway"), QMessageBox::AcceptRole); - dialog.setDefaultButton(justContinue); - dialog.setWindowTitle(tr("Debugging helper missing")); - dialog.setText(tr("The debugger did not find the debugging helper library.")); - dialog.setInformativeText(tr("The debugging helper is used to nicely format the values of Qt " - "data types and some STL data types. " - "It must be compiled for each Qt version, " - "you can do this in the Qt preferences page by selecting " - "a Qt installation and clicking on 'Rebuild' for the debugging " - "helper.")); - dialog.exec(); - if (dialog.clickedButton() == qtPref) { - Core::ICore::instance()->showOptionsDialog("Qt4", "Qt Versions"); - } else if (dialog.clickedButton() == helperOff) { - theDebuggerAction(UseDebuggingHelpers)->setValue(qVariantFromValue(false), false); - } -} - void GdbEngine::tryLoadDebuggingHelpers() { if (m_debuggingHelperState != DebuggingHelperUninitialized) @@ -4165,16 +4135,16 @@ void GdbEngine::tryLoadDebuggingHelpers() PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS"); m_debuggingHelperState = DebuggingHelperUnavailable; - QString lib = dumperLibraryName(); + if (!q->qtDumperLibraryEnabled()) + return; + const QString lib = q->qtDumperLibraryName(); //qDebug() << "DUMPERLIB: " << lib; - - if (!QFileInfo(lib).exists()) { - debugMessage(QString("DEBUG HELPER LIBRARY IS NOT USABLE: " - " %1 EXISTS: %2, EXECUTABLE: %3").arg(lib) - .arg(QFileInfo(lib).exists()) - .arg(QFileInfo(lib).isExecutable())); - if (theDebuggerBoolSetting(UseDebuggingHelpers)) - showDebuggingHelperWarning(); + // @TODO: same in CDB engine... + const QFileInfo fi(lib); + if (!fi.exists()) { + const QString msg = tr("The dumper library '%1' does not exist.").arg(lib); + debugMessage(msg); + q->showQtDumperLibraryWarning(msg); return; } diff --git a/src/plugins/debugger/gdbengine.h b/src/plugins/debugger/gdbengine.h index 4cb7dcef6590a3f263e929cb7c95b7579e020521..67a7c9a24249154bb8824811cd0467bb764a33c5 100644 --- a/src/plugins/debugger/gdbengine.h +++ b/src/plugins/debugger/gdbengine.h @@ -137,7 +137,7 @@ private: // // Own stuff // - void showDebuggingHelperWarning(); + int currentFrame() const; QString currentWorkingDirectory() const { return m_pwd; } @@ -202,7 +202,6 @@ private: void handleTargetCore(const GdbResultRecord &response); void handleExit(const GdbResultRecord &response); void debugMessage(const QString &msg); - QString dumperLibraryName() const; OutputCollector m_outputCollector; QTextCodec *m_outputCodec;