diff --git a/bin/qtcreator b/bin/qtcreator index d00cfa0f582d6b4d6bc175fc516259f7a2b7505b..f66b08778a6692513342d706daf82102b0c884b4 100755 --- a/bin/qtcreator +++ b/bin/qtcreator @@ -1,9 +1,9 @@ #!/bin/sh -bindir=$(dirname $(readlink -nf $0)) +bindir=$(dirname "$(readlink -nf $0)") if test "$(uname -m)" = "x86_64" ; then - libdir=$(cd ${bindir}/../lib64 ; pwd) + libdir=$(cd "${bindir}/../lib64" ; pwd) else - libdir=$(cd ${bindir}/../lib ; pwd) + libdir=$(cd "${bindir}/../lib" ; pwd) fi LD_LIBRARY_PATH="${libdir}/qtcreator:${LD_LIBRARY_PATH}" exec "${bindir}/qtcreator.bin" ${1+"$@"} diff --git a/src/plugins/debugger/breakwindow.cpp b/src/plugins/debugger/breakwindow.cpp index a571fafae506cdd1d1f34c16786e99fb1ca493fd..a93885abcb1ffb5dad2147eaf1702f21e824e2d1 100644 --- a/src/plugins/debugger/breakwindow.cpp +++ b/src/plugins/debugger/breakwindow.cpp @@ -84,16 +84,17 @@ void BreakWindow::resizeEvent(QResizeEvent *ev) void BreakWindow::contextMenuEvent(QContextMenuEvent *ev) { QMenu menu; - QModelIndex index = indexAt(ev->pos()); + const QModelIndex index = indexAt(ev->pos()); + const bool indexIsValid = index.isValid(); QAction *act0 = new QAction(tr("Delete breakpoint"), &menu); - act0->setEnabled(index.isValid()); + act0->setEnabled(indexIsValid); QAction *act1 = new QAction(tr("Adjust column widths to contents"), &menu); QAction *act2 = new QAction(tr("Always adjust column widths to contents"), &menu); act2->setCheckable(true); act2->setChecked(m_alwaysResizeColumnsToContents); QAction *act3 = new QAction(tr("Edit condition..."), &menu); - act0->setEnabled(index.isValid()); - QAction *act4 = new QAction(tr("Syncronize breakpoints"), &menu); + act3->setEnabled(indexIsValid); + QAction *act4 = new QAction(tr("Synchronize breakpoints"), &menu); menu.addAction(act0); menu.addAction(act3); diff --git a/src/plugins/debugger/cdb/cdb.pri b/src/plugins/debugger/cdb/cdb.pri index a9e9a5cddfa7c4ff3cd646cdd00ce46004f56bb1..97932b310269369ca654d94e36de9c92a03d57f6 100644 --- a/src/plugins/debugger/cdb/cdb.pri +++ b/src/plugins/debugger/cdb/cdb.pri @@ -6,6 +6,10 @@ contains(QMAKE_CXX, cl) { CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows/sdk" +!exists ($$CDB_PATH) { + CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x86)/sdk" +} + exists ($$CDB_PATH) { message("Experimental: Adding support for $$CDB_PATH") @@ -25,7 +29,9 @@ HEADERS += \ $$PWD/cdbdebugoutput.h \ $$PWD/cdbsymbolgroupcontext.h \ $$PWD/cdbstacktracecontext.h \ - $$PWD/cdbbreakpoint.h + $$PWD/cdbbreakpoint.h \ + $$PWD/cdbmodules.h \ + $$PWD/cdbassembler.h SOURCES += \ $$PWD/cdbdebugengine.cpp \ @@ -33,7 +39,9 @@ SOURCES += \ $$PWD/cdbdebugoutput.cpp \ $$PWD/cdbsymbolgroupcontext.cpp \ $$PWD/cdbstacktracecontext.cpp \ - $$PWD/cdbbreakpoint.cpp + $$PWD/cdbbreakpoint.cpp \ + $$PWD/cdbmodules.cpp \ + $$PWD/cdbassembler.cpp } else { message("Debugging Tools for Windows could not be found in $$CDB_PATH") diff --git a/src/plugins/debugger/cdb/cdbassembler.cpp b/src/plugins/debugger/cdb/cdbassembler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4cb59c7d69f018d7d9f721c8b17cc5a3fa267e45 --- /dev/null +++ b/src/plugins/debugger/cdb/cdbassembler.cpp @@ -0,0 +1,234 @@ +/************************************************************************** +** +** 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 "cdbassembler.h" +#include "cdbdebugoutput.h" +#include "cdbdebugengine_p.h" +#include "cdbsymbolgroupcontext.h" + +#include "disassemblerhandler.h" +#include "registerhandler.h" + +#include <QtCore/QVector> + +namespace Debugger { +namespace Internal { + +typedef QList<DisassemblerLine> DisassemblerLineList; + +bool getRegisters(IDebugControl4 *ctl, + IDebugRegisters2 *ireg, + QList<Register> *registers, + QString *errorMessage, int base) +{ + registers->clear(); + ULONG count; + HRESULT hr = ireg->GetNumberRegisters(&count); + if (FAILED(hr)) { + *errorMessage= msgComFailed("GetNumberRegisters", hr); + return false; + } + if (!count) + return true; + // Retrieve names + WCHAR wszBuf[MAX_PATH]; + for (ULONG r = 0; r < count; r++) { + hr = ireg->GetDescriptionWide(r, wszBuf, MAX_PATH - 1, 0, 0); + if (FAILED(hr)) { + *errorMessage= msgComFailed("GetDescriptionWide", hr); + return false; + } + Register reg; + reg.name = QString::fromUtf16(wszBuf); + registers->push_back(reg); + } + // get values + QVector<DEBUG_VALUE> values(count); + DEBUG_VALUE *valuesPtr = &(*values.begin()); + memset(valuesPtr, 0, count * sizeof(DEBUG_VALUE)); + hr = ireg->GetValues(count, 0, 0, valuesPtr); + if (FAILED(hr)) { + *errorMessage= msgComFailed("GetValues", hr); + return false; + } + if (base < 2) + base = 10; + for (ULONG r = 0; r < count; r++) + (*registers)[r].value = CdbSymbolGroupContext::debugValueToString(values.at(r), ctl, 0, base); + return true; +} + +// Output parser for disassembler lines. +// It uses the source file lines as symbol until it encounters +// a C++ symbol (function entered), from which then on +// it uses that symbol. +class DisassemblerOutputParser +{ +public: + explicit DisassemblerOutputParser(DisassemblerLineList *list); + + void parse(const QStringList &l); + +private: + enum ParseResult { ParseOk, ParseIgnore, ParseFailed }; + ParseResult parseDisassembled(const QString &in, DisassemblerLine* l); + + DisassemblerLineList *m_list; + QString m_sourceSymbol; + int m_sourceSymbolOffset; +}; + +DisassemblerOutputParser::DisassemblerOutputParser(DisassemblerLineList *list) : + m_list(list), + m_sourceSymbolOffset(0) +{ +} + +// Parse a disassembler line: +// module!class::foo: +// 004017cf cc int 3 +// 77 mainwindow.cpp 004018ff 8d4da8 lea ecx,[ebp-0x58] +DisassemblerOutputParser::ParseResult + DisassemblerOutputParser::parseDisassembled(const QString &in, DisassemblerLine* l) +{ + l->clear(); + + // Check if there is a source file + if (in.size() < 7) + return ParseIgnore; + const bool hasSourceFile = !in.at(6).isSpace(); + + // Sometimes, empty lines occur + const QString simplified = in.simplified(); + if (simplified.isEmpty()) + return ParseIgnore; + + QStringList tokens = simplified.split(QLatin1Char(' '), QString::SkipEmptyParts); + // Check for symbols as 'module!class::foo:' (start of function encountered) + if (tokens.size() == 1) { + QString symbol = tokens.front(); + if (symbol.endsWith(QLatin1Char(':')) && symbol.contains(QLatin1Char('!'))) { + symbol.truncate(symbol.size() - 1); + m_sourceSymbol = symbol; + m_sourceSymbolOffset = 0; + } + return ParseIgnore; + } + if (tokens.size() < 2) + return ParseIgnore; + // Symbol display: Do we know a symbol? + if (!m_sourceSymbol.isEmpty()) { + l->symbol = QString(QLatin1Char('<')); + l->symbol += m_sourceSymbol; + if (m_sourceSymbolOffset) { + l->symbol += QLatin1Char('+'); + l->symbol += QString::number(m_sourceSymbolOffset); + } + l->symbol += QLatin1Char('>'); + m_sourceSymbolOffset++; + } + // Read source file information: If we don't know a symbol yet, + // use the source file. + if (hasSourceFile) { + if (l->symbol.isEmpty()) { + l->symbol = tokens.at(1); + l->symbol += QLatin1Char('+'); + l->symbol += tokens.front(); + } + tokens.pop_front(); + tokens.pop_front(); + } + l->symbolDisplay = l->symbol; + // Get offset address and instruction + if (tokens.size() < 3) + return ParseFailed; + l->addressDisplay = l->address = tokens.front(); + tokens.pop_front(); + // The rest is effective address & instructions + if (tokens.size() > 1) + tokens.pop_front(); + l->mnemonic = tokens.join(QString(QLatin1Char(' '))); + return ParseOk; +} + +void DisassemblerOutputParser::parse(const QStringList &l) +{ + DisassemblerLine dLine; + foreach(const QString &line, l) { + switch (parseDisassembled(line, &dLine)) { + case ParseOk: + m_list->push_back(dLine); + break; + case ParseIgnore: + break; + case ParseFailed: + qWarning("Failed to parse '%s'\n", qPrintable(line)); + break; + } + } +} + +bool dissassemble(IDebugClient5 *client, + IDebugControl4 *ctl, + ULONG64 offset, + unsigned long beforeLines, + unsigned long afterLines, + QList<DisassemblerLine> *lines, + QString *errorMessage) +{ + if (debugCDB) + qDebug() << Q_FUNC_INFO << offset; + lines->clear(); + const ULONG flags = DEBUG_DISASM_MATCHING_SYMBOLS|DEBUG_DISASM_SOURCE_LINE_NUMBER|DEBUG_DISASM_SOURCE_FILE_NAME; + // Catch the output by temporarily setting another handler. + // We use the method that outputs to the output handler as it + // conveniently provides the 'beforeLines' context (stepping back + // 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); + // 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)); + return false; + } + DisassemblerOutputParser parser(lines); + parser.parse(stringHandler.result().split(QLatin1Char('\n'))); + return true; +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbassembler.h b/src/plugins/debugger/cdb/cdbassembler.h new file mode 100644 index 0000000000000000000000000000000000000000..e43492ebaf2dccb0a16aa5581e237728bf8a3003 --- /dev/null +++ b/src/plugins/debugger/cdb/cdbassembler.h @@ -0,0 +1,64 @@ +/************************************************************************** +** +** 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 CDBASSEMBLER_H +#define CDBASSEMBLER_H + +#include <QtCore/QList> +#include <QtCore/QString> + +#include <windows.h> +#include <inc/dbgeng.h> + +namespace Debugger { +namespace Internal { + +class DisassemblerLine; + +// Utilities related to assembler code. +class Register; + +bool getRegisters(IDebugControl4 *ctl, + IDebugRegisters2 *ireg, + QList<Register> *registers, + QString *errorMessage, + int base = 10 /* 16 for hex, etc */); + +bool dissassemble(IDebugClient5 *client, + IDebugControl4 *ctl, + ULONG64 offset, + unsigned long beforeLines, + unsigned long afterLines, + QList<DisassemblerLine> *lines, + QString *errorMessage); + +} // namespace Internal +} // namespace Debugger + +#endif // CDBASSEMBLER_H diff --git a/src/plugins/debugger/cdb/cdbbreakpoint.cpp b/src/plugins/debugger/cdb/cdbbreakpoint.cpp index 035c5d76fca5dc19baaf6f0449cae2dbe518cd1d..6ef96f5597ad9fe31a1c6134c8e7228018c4dccf 100644 --- a/src/plugins/debugger/cdb/cdbbreakpoint.cpp +++ b/src/plugins/debugger/cdb/cdbbreakpoint.cpp @@ -28,6 +28,7 @@ **************************************************************************/ #include "cdbbreakpoint.h" +#include "cdbmodules.h" #include "breakhandler.h" #include "cdbdebugengine_p.h" @@ -279,20 +280,48 @@ bool CDBBreakPoint::getBreakPoints(IDebugControl4* debugControl, QList<CDBBreakP return true; } + // Synchronize (halted) engine breakpoints with those of the BreakHandler. bool CDBBreakPoint::synchronizeBreakPoints(IDebugControl4* debugControl, + IDebugSymbols3 *syms, BreakHandler *handler, QString *errorMessage) { typedef QMap<CDBBreakPoint, int> BreakPointIndexMap; - BreakPointIndexMap breakPointIndexMap; - // convert BreakHandler's bps into a map of BreakPoint->BreakHandler->Index if (debugCDB) qDebug() << Q_FUNC_INFO; + BreakPointIndexMap breakPointIndexMap; + // convert BreakHandler's bps into a map of BreakPoint->BreakHandler->Index + // Ignore invalid functions (that could not be found) as they make + // the debugger hang. const int handlerCount = handler->size(); - for (int i=0; i < handlerCount; ++i) - breakPointIndexMap.insert(CDBBreakPoint(*handler->at(i)), i); + const QChar moduleDelimiter = QLatin1Char('!'); + for (int i=0; i < handlerCount; ++i) { + BreakpointData *bd = handler->at(i); + // Function breakpoints: Are the module names specified? + bool breakPointOk = false; + if (bd->funcName.isEmpty()) { + breakPointOk = true; + } else { + switch (resolveSymbol(syms, &bd->funcName, errorMessage)) { + case ResolveSymbolOk: + breakPointOk = true; + break; + case ResolveSymbolAmbiguous: + qWarning("Warning: %s\n", qPrintable(*errorMessage)); + breakPointOk = true; + break; + case ResolveSymbolNotFound: + case ResolveSymbolError: + qWarning("Warning: %s\n", qPrintable(*errorMessage)); + break; + }; + } // function breakpoint + if (breakPointOk) + breakPointIndexMap.insert(CDBBreakPoint(*bd), i); + } + errorMessage->clear(); // get number of engine breakpoints ULONG engineCount; if (!getBreakPointCount(debugControl, &engineCount, errorMessage)) @@ -356,5 +385,5 @@ bool CDBBreakPoint::synchronizeBreakPoints(IDebugControl4* debugControl, return true; } -} -} +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbbreakpoint.h b/src/plugins/debugger/cdb/cdbbreakpoint.h index 2abdddcb94273e211e4dfe80de4b497eb5ebecd5..3d925b2577e2bc251bfd803300d15d979310db7c 100644 --- a/src/plugins/debugger/cdb/cdbbreakpoint.h +++ b/src/plugins/debugger/cdb/cdbbreakpoint.h @@ -49,7 +49,8 @@ class BreakpointData; /* CDB Break point data structure with utilities to * apply to engine and to retrieve them from the engine and comparison. */ -struct CDBBreakPoint { +struct CDBBreakPoint +{ CDBBreakPoint(); CDBBreakPoint(const BreakpointData &bpd); @@ -72,7 +73,8 @@ struct CDBBreakPoint { static bool getBreakPointCount(IDebugControl4* debugControl, ULONG *count, QString *errorMessage = 0); static bool getBreakPoints(IDebugControl4* debugControl, QList<CDBBreakPoint> *bps, QString *errorMessage); // Synchronize (halted) engine with BreakHandler. - static bool synchronizeBreakPoints(IDebugControl4* ctl, BreakHandler *bh, QString *errorMessage); + static bool synchronizeBreakPoints(IDebugControl4* ctl, IDebugSymbols3 *syms, + BreakHandler *bh, QString *errorMessage); // Return a 'canonical' file (using '/' and capitalized drive letter) static QString canonicalSourceFile(const QString &f); @@ -93,7 +95,7 @@ inline bool operator!=(const CDBBreakPoint& b1, const CDBBreakPoint& b2) inline bool operator<(const CDBBreakPoint& b1, const CDBBreakPoint& b2) { return b1.compare(b2) < 0; } -} -} +} // namespace Internal +} // namespace Debugger #endif // CDBBREAKPOINTS_H diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index a85069cc0ef7572af87e1a4fc18afcbafc8e4e96..a71aa2ac721553f596f1de9c6d835de54380a397 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -32,11 +32,17 @@ #include "cdbsymbolgroupcontext.h" #include "cdbstacktracecontext.h" #include "cdbbreakpoint.h" +#include "cdbmodules.h" +#include "cdbassembler.h" +#include "debuggeractions.h" #include "debuggermanager.h" #include "breakhandler.h" #include "stackhandler.h" #include "watchhandler.h" +#include "registerhandler.h" +#include "moduleshandler.h" +#include "disassemblerhandler.h" #include "watchutils.h" #include <utils/qtcassert.h> @@ -51,6 +57,7 @@ #include <QtCore/QCoreApplication> #include <QtGui/QMessageBox> #include <QtGui/QMainWindow> +#include <QtGui/QApplication> #define DBGHELP_TRANSLATE_TCHAR #include <inc/Dbghelp.h> @@ -190,7 +197,6 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent, CdbDebugEn m_breakEventMode(BreakEventHandle), m_watchTimer(-1), m_debugEventCallBack(engine), - m_debugOutputCallBack(engine), m_pDebugClient(0), m_pDebugControl(0), m_pDebugSystemObjects(0), @@ -202,7 +208,7 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent, CdbDebugEn m_currentStackTrace(0), m_firstActivatedFrame(true), m_mode(AttachCore) -{ +{ } bool CdbDebugEnginePrivate::init(QString *errorMessage) @@ -220,7 +226,7 @@ bool CdbDebugEnginePrivate::init(QString *errorMessage) return false; } - m_pDebugClient->SetOutputCallbacks(&m_debugOutputCallBack); + m_pDebugClient->SetOutputCallbacksWide(&m_debugOutputCallBack); m_pDebugClient->SetEventCallbacks(&m_debugEventCallBack); hr = lib.debugCreate( __uuidof(IDebugControl4), reinterpret_cast<void**>(&m_pDebugControl)); @@ -246,7 +252,7 @@ bool CdbDebugEnginePrivate::init(QString *errorMessage) hr = lib.debugCreate( __uuidof(IDebugRegisters2), reinterpret_cast<void**>(&m_pDebugRegisters)); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Creation of IDebugRegisters2 failed: %1").arg(msgDebugEngineComResult(hr)); - return false; + return false; } if (debugCDB) qDebug() << QString::fromLatin1("CDB Initialization succeeded, interrupt time out %1s.").arg(getInterruptTimeOutSecs(m_pDebugControl)); @@ -293,7 +299,7 @@ void CdbDebugEnginePrivate::clearForRun() } void CdbDebugEnginePrivate::cleanStackTrace() -{ +{ if (m_currentStackTrace) { delete m_currentStackTrace; m_currentStackTrace = 0; @@ -308,6 +314,14 @@ CdbDebugEngine::CdbDebugEngine(DebuggerManager *parent) : connect(&m_d->m_consoleStubProc, SIGNAL(processError(QString)), this, SLOT(slotConsoleStubError(QString))); connect(&m_d->m_consoleStubProc, SIGNAL(processStarted()), this, SLOT(slotConsoleStubStarted())); connect(&m_d->m_consoleStubProc, SIGNAL(wrapperStopped()), this, SLOT(slotConsoleStubTerminated())); + connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggerOutput(QString,QString)), + m_d->m_debuggerManager, SLOT(showDebuggerOutput(QString,QString))); + connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggerInputPrompt(QString,QString)), + m_d->m_debuggerManager, SLOT(showDebuggerInput(QString,QString))); + connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggeeOutput(QString)), + m_d->m_debuggerManager, SLOT(showApplicationOutput(QString))); + connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggeeInputPrompt(QString)), + m_d->m_debuggerManager, SLOT(showApplicationOutput(QString))); } CdbDebugEngine::~CdbDebugEngine() @@ -344,8 +358,16 @@ void CdbDebugEngine::setToolTipExpression(const QPoint & /*pos*/, const QString { } +void CdbDebugEnginePrivate::clearDisplay() +{ + m_debuggerManagerAccess->threadsHandler()->removeAll(); + m_debuggerManagerAccess->modulesHandler()->removeAll(); + m_debuggerManagerAccess->registerHandler()->removeAll(); +} + bool CdbDebugEngine::startDebugger() -{ +{ + m_d->clearDisplay(); m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1); QString errorMessage; bool rc = false; @@ -467,7 +489,7 @@ void CdbDebugEngine::exitDebugger() // Terminate or detach if we are running HRESULT hr; switch (m_d->m_mode) { - case AttachExternal: + case AttachExternal: wasRunning = m_d->isDebuggeeRunning(); if (wasRunning) { // Process must be stopped in order to detach m_d->interruptInterferiorProcess(&errorMessage); @@ -480,7 +502,7 @@ void CdbDebugEngine::exitDebugger() qDebug() << Q_FUNC_INFO << "detached" << msgDebugEngineComResult(hr); break; case StartExternal: - case StartInternal: + case StartInternal: wasRunning = m_d->isDebuggeeRunning(); if (wasRunning) { // Process must be stopped in order to terminate m_d->interruptInterferiorProcess(&errorMessage); @@ -547,7 +569,7 @@ bool CdbDebugEnginePrivate::updateLocals(int frameIndex, qDebug() << Q_FUNC_INFO << "\n " << frameIndex << formatWatchList(incompletes); m_engine->filterEvaluateWatchers(&incompletes, wh); - if (!incompletes.empty()) { + if (!incompletes.empty()) { const QString msg = QLatin1String("Warning: Locals left in incomplete list: ") + formatWatchList(incompletes); qWarning("%s\n", qPrintable(msg)); } @@ -591,7 +613,7 @@ void CdbDebugEngine::filterEvaluateWatchers(QList<WatchData> *wd, WatchHandler * bool placeHolderSeen = false; for (WatchList::iterator it = wd->begin(); it != wd->end(); ) { if (it->iname.startsWith(watcherPrefix)) { - const bool isPlaceHolder = it->exp.startsWith(lessThan) && it->exp.endsWith(greaterThan); + const bool isPlaceHolder = it->exp.startsWith(lessThan) && it->exp.endsWith(greaterThan); if (isPlaceHolder) { if (!placeHolderSeen) { // Max one place holder it->setChildCount(0); @@ -602,7 +624,7 @@ void CdbDebugEngine::filterEvaluateWatchers(QList<WatchData> *wd, WatchHandler * } else { evaluateWatcher(&(*it)); wh->insertData(*it); - } + } it = wd->erase(it); } else { ++it; @@ -756,7 +778,7 @@ bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessage) // Continue process with notifications bool CdbDebugEnginePrivate::continueInferior(QString *errorMessage) -{ +{ ULONG executionStatus; if (!getExecutionStatus(m_pDebugControl, &executionStatus, errorMessage)) return false; @@ -795,7 +817,7 @@ bool CdbDebugEnginePrivate::interruptInterferiorProcess(QString *errorMessage) *errorMessage = QString::fromLatin1("DebugBreakProcess failed: %1").arg(Core::Utils::winErrorMessage(GetLastError())); return false; } -#if 0 +#if 0 const HRESULT hr = m_pDebugControl->SetInterrupt(DEBUG_INTERRUPT_ACTIVE|DEBUG_INTERRUPT_EXIT); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Unable to interrupt debuggee after %1s: %2"). @@ -842,7 +864,7 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v if (debugCDB) qDebug() << Q_FUNC_INFO << expr << value; const int frameIndex = m_d->m_debuggerManagerAccess->stackHandler()->currentIndex(); - QString errorMessage; + QString errorMessage; bool success = false; do { QString newValue; @@ -850,7 +872,7 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v if (!sg) break; if (!sg->assignValue(expr, value, &newValue, &errorMessage)) - break; + break; // Update view WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler(); if (WatchData *fwd = watchHandler->findData(expr)) { @@ -1010,6 +1032,7 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa } return CDBBreakPoint::synchronizeBreakPoints(m_pDebugControl, + m_pDebugSymbols, m_debuggerManagerAccess->breakHandler(), errorMessage); } @@ -1024,6 +1047,30 @@ void CdbDebugEngine::saveSessionData() void CdbDebugEngine::reloadDisassembler() { + enum { ContextLines = 40 }; + // Do we have a top stack frame? + const ULONG64 offset = m_d->m_currentStackTrace ? m_d->m_currentStackTrace->instructionOffset() : ULONG64(0); + if (debugCDB) + qDebug() << Q_FUNC_INFO << offset; + + DisassemblerHandler *dh = m_d->m_debuggerManagerAccess->disassemblerHandler(); + if (offset) { + QList<DisassemblerLine> lines; + QString errorMessage; + QApplication::setOverrideCursor(Qt::WaitCursor); + const bool drc = dissassemble(m_d->m_pDebugClient, m_d->m_pDebugControl, offset, + ContextLines, ContextLines, &lines, &errorMessage); + QApplication::restoreOverrideCursor(); + if (drc) { + dh->setLines(lines); + if (lines.size() > ContextLines) + dh->setCurrentLine(ContextLines); + } else { + qWarning("reloadDisassembler: %s\n", qPrintable(errorMessage)); + } + } else { + dh->setLines(QList<DisassemblerLine>()); + } } void CdbDebugEngine::reloadModules() @@ -1042,8 +1089,54 @@ void CdbDebugEngine::loadAllSymbols() qDebug() << Q_FUNC_INFO; } +QList<Symbol> CdbDebugEngine::moduleSymbols(const QString &moduleName) +{ + QList<Symbol> rc; + QString errorMessage; + bool success = false; + do { + if (m_d->isDebuggeeRunning()) { + errorMessage = tr("Cannot retrieve symbols while the debuggee is running."); + break; + } + if (!getModuleSymbols(m_d->m_pDebugSymbols, moduleName, &rc, &errorMessage)) + break; + success = true; + } while (false); + if (!success) + qWarning("%s\n", qPrintable(errorMessage)); + return rc; +} + +static inline int registerFormatBase() +{ + switch(checkedRegisterFormatAction()) { + case FormatHexadecimal: + return 16; + case FormatDecimal: + return 10; + case FormatOctal: + return 8; + case FormatBinary: + return 2; + break; + case FormatRaw: + case FormatNatural: + break; + } + return 10; +} + void CdbDebugEngine::reloadRegisters() { + const int intBase = registerFormatBase(); + if (debugCDB) + qDebug() << Q_FUNC_INFO << intBase; + QList<Register> registers; + QString errorMessage; + if (!getRegisters(m_d->m_pDebugControl, m_d->m_pDebugRegisters, ®isters, &errorMessage, intBase)) + qWarning("reloadRegisters() failed: %s\n", qPrintable(errorMessage)); + m_d->m_debuggerManagerAccess->registerHandler()->setRegisters(registers); } void CdbDebugEngine::timerEvent(QTimerEvent* te) @@ -1067,7 +1160,7 @@ void CdbDebugEngine::timerEvent(QTimerEvent* te) break; case E_UNEXPECTED: // Occurs on ExitProcess. killWatchTimer(); - break; + break; } } @@ -1142,21 +1235,34 @@ void CdbDebugEnginePrivate::updateThreadList() ThreadsHandler* th = m_debuggerManagerAccess->threadsHandler(); QList<ThreadData> threads; + bool success = false; + QString errorMessage; + do { + ULONG numberOfThreads; + HRESULT hr= m_pDebugSystemObjects->GetNumberThreads(&numberOfThreads); + if (FAILED(hr)) { + errorMessage= msgComFailed("GetNumberThreads", hr); + break; + } + const ULONG maxThreadIds = 256; + ULONG threadIds[maxThreadIds]; + ULONG biggestThreadId = qMin(maxThreadIds, numberOfThreads - 1); + hr = m_pDebugSystemObjects->GetThreadIdsByIndex(0, biggestThreadId, threadIds, 0); + if (FAILED(hr)) { + errorMessage= msgComFailed("GetThreadIdsByIndex", hr); + break; + } + for (ULONG threadId = 0; threadId <= biggestThreadId; ++threadId) { + ThreadData thread; + thread.id = threadId; + threads.append(thread); + } - HRESULT hr; - ULONG numberOfThreads; - hr = m_pDebugSystemObjects->GetNumberThreads(&numberOfThreads); - const ULONG maxThreadIds = 256; - ULONG threadIds[maxThreadIds]; - ULONG biggestThreadId = qMin(maxThreadIds, numberOfThreads - 1); - hr = m_pDebugSystemObjects->GetThreadIdsByIndex(0, biggestThreadId, threadIds, 0); - for (ULONG threadId = 0; threadId <= biggestThreadId; ++threadId) { - ThreadData thread; - thread.id = threadId; - threads.append(thread); - } - - th->setThreads(threads); + th->setThreads(threads); + success = true; + } while (false); + if (!success) + qWarning("updateThreadList() failed: %s\n", qPrintable(errorMessage)); } void CdbDebugEnginePrivate::updateStackTrace() @@ -1166,6 +1272,7 @@ void CdbDebugEnginePrivate::updateStackTrace() // Create a new context clearForRun(); QString errorMessage; + m_engine->reloadRegisters(); m_currentStackTrace = CdbStackTraceContext::create(m_pDebugControl, m_pDebugSystemObjects, m_pDebugSymbols, m_currentThreadId, &errorMessage); @@ -1173,6 +1280,10 @@ void CdbDebugEnginePrivate::updateStackTrace() qWarning("%s: failed to create trace context: %s", Q_FUNC_INFO, qPrintable(errorMessage)); return; } + // Disassembling slows things down a bit. Assembler is still available via menu. +#if 0 + m_engine->reloadDisassembler(); // requires stack trace +#endif const QList<StackFrame> stackFrames = m_currentStackTrace->frames(); // find the first usable frame and select it int current = -1; @@ -1188,14 +1299,17 @@ void CdbDebugEnginePrivate::updateStackTrace() if (current >= 0) { m_debuggerManagerAccess->stackHandler()->setCurrentIndex(current); m_engine->activateFrame(current); - } + } } -void CdbDebugEnginePrivate::handleDebugOutput(const char *szOutputString) + +void CdbDebugEnginePrivate::updateModules() { - if (debugCDB && strstr(szOutputString, "ModLoad:") == 0) - qDebug() << Q_FUNC_INFO << szOutputString; - m_debuggerManagerAccess->showApplicationOutput(QString::fromLocal8Bit(szOutputString)); + QList<Module> modules; + QString errorMessage; + if (!getModuleList(m_pDebugSymbols, &modules, &errorMessage)) + qWarning("updateModules() failed: %s\n", qPrintable(errorMessage)); + m_debuggerManagerAccess->modulesHandler()->setModules(modules); } void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT pBP) diff --git a/src/plugins/debugger/cdb/cdbdebugengine.h b/src/plugins/debugger/cdb/cdbdebugengine.h index f37bade1cd3db6850bb36e1f1ccc53c174d9cd21..fe91fef5df8a150bf5e240d15e3a22c6cefbfd28 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.h +++ b/src/plugins/debugger/cdb/cdbdebugengine.h @@ -87,6 +87,7 @@ public: virtual void reloadModules(); virtual void loadSymbols(const QString &moduleName); virtual void loadAllSymbols(); + virtual QList<Symbol> moduleSymbols(const QString &moduleName); virtual void reloadRegisters(); virtual void reloadSourceFiles(); diff --git a/src/plugins/debugger/cdb/cdbdebugengine_p.h b/src/plugins/debugger/cdb/cdbdebugengine_p.h index 98e725cfbfd40a963a8ab2f1d2ead76ae9fb8efa..ce9c2e381bf9082c9bd7dade9c3cf1712f5e7ae5 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine_p.h +++ b/src/plugins/debugger/cdb/cdbdebugengine_p.h @@ -48,7 +48,8 @@ class CdbStackTraceContext; // Thin wrapper around the 'DBEng' debugger engine shared library // which is loaded at runtime. -class DebuggerEngineLibrary { +class DebuggerEngineLibrary +{ public: DebuggerEngineLibrary(); bool init(QString *errorMessage); @@ -79,14 +80,16 @@ struct CdbDebugEnginePrivate bool isDebuggeeRunning() const { return m_watchTimer != -1; } void handleDebugEvent(); - void updateThreadList(); + void updateThreadList(); void updateStackTrace(); bool updateLocals(int frameIndex, WatchHandler *wh, QString *errorMessage); - void handleDebugOutput(const char* szOutputString); + void updateModules(); + void handleBreakpointEvent(PDEBUG_BREAKPOINT pBP); void cleanStackTrace(); void clearForRun(); CdbSymbolGroupContext *getStackFrameSymbolGroupContext(int frameIndex, QString *errorMessage) const; + void clearDisplay(); bool interruptInterferiorProcess(QString *errorMessage); diff --git a/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp b/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp index 14325f8ed5561499dc17ec8aa5493483627335e4..526902e2c3d8e3603925178335c648c082b33d89 100644 --- a/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp +++ b/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp @@ -76,8 +76,9 @@ STDMETHODIMP_(ULONG) CdbDebugEventCallback::Release(THIS) STDMETHODIMP CdbDebugEventCallback::GetInterestMask(THIS_ __out PULONG mask) { - *mask = DEBUG_EVENT_CREATE_PROCESS | DEBUG_EVENT_EXIT_PROCESS - //| DEBUG_EVENT_CREATE_THREAD | DEBUG_EVENT_EXIT_THREAD + *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 ; @@ -125,13 +126,9 @@ STDMETHODIMP CdbDebugEventCallback::CreateThread( Q_UNUSED(Handle) Q_UNUSED(DataOffset) Q_UNUSED(StartOffset) - if (debugCDB) qDebug() << Q_FUNC_INFO; - //Debugger::ThreadInfo ti; - //ti.handle = Handle; - //ti.dataOffset = DataOffset; - //ti.startOffset = StartOffset; + m_pEngine->m_d->updateThreadList(); return S_OK; } @@ -142,7 +139,8 @@ STDMETHODIMP CdbDebugEventCallback::ExitThread( { if (debugCDB) qDebug() << Q_FUNC_INFO << ExitCode; - + // @TODO: It seems the terminated thread is still in the list... + m_pEngine->m_d->updateThreadList(); return S_OK; } @@ -211,14 +209,14 @@ STDMETHODIMP CdbDebugEventCallback::LoadModule( { Q_UNUSED(ImageFileHandle) Q_UNUSED(BaseOffset) - Q_UNUSED(ModuleSize) Q_UNUSED(ModuleName) + Q_UNUSED(ModuleSize) Q_UNUSED(ImageName) Q_UNUSED(CheckSum) Q_UNUSED(TimeDateStamp) - if (debugCDB) + if (debugCDB > 1) qDebug() << Q_FUNC_INFO << ModuleName; - + m_pEngine->m_d->updateModules(); return S_OK; } @@ -230,9 +228,9 @@ STDMETHODIMP CdbDebugEventCallback::UnloadModule( { Q_UNUSED(ImageBaseName) Q_UNUSED(BaseOffset) - if (debugCDB) + if (debugCDB > 1) qDebug() << Q_FUNC_INFO << ImageBaseName; - + m_pEngine->m_d->updateModules(); return S_OK; } diff --git a/src/plugins/debugger/cdb/cdbdebugoutput.cpp b/src/plugins/debugger/cdb/cdbdebugoutput.cpp index 0bfa568fc707650ef76d555d452a1b84a251d847..114c59c7712b31733d3ae99f6e85c6434e8ef1a4 100644 --- a/src/plugins/debugger/cdb/cdbdebugoutput.cpp +++ b/src/plugins/debugger/cdb/cdbdebugoutput.cpp @@ -27,7 +27,6 @@ ** **************************************************************************/ - #include "cdbdebugoutput.h" #include "cdbdebugengine.h" #include "cdbdebugengine_p.h" @@ -38,12 +37,11 @@ namespace Debugger { namespace Internal { -CdbDebugOutput::CdbDebugOutput(CdbDebugEngine* engine) : - m_pEngine(engine) +CdbDebugOutputBase::CdbDebugOutputBase() { } -STDMETHODIMP CdbDebugOutput::QueryInterface( +STDMETHODIMP CdbDebugOutputBase::QueryInterface( THIS_ IN REFIID InterfaceId, OUT PVOID* Interface @@ -52,9 +50,9 @@ STDMETHODIMP CdbDebugOutput::QueryInterface( *Interface = NULL; if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) || - IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks))) + IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacksWide))) { - *Interface = (IDebugOutputCallbacks *)this; + *Interface = (IDebugOutputCallbacksWide*)this; AddRef(); return S_OK; } else { @@ -62,30 +60,100 @@ STDMETHODIMP CdbDebugOutput::QueryInterface( } } -STDMETHODIMP_(ULONG) CdbDebugOutput::AddRef(THIS) +STDMETHODIMP_(ULONG) CdbDebugOutputBase::AddRef(THIS) { // This class is designed to be static so // there's no true refcount. return 1; } -STDMETHODIMP_(ULONG) CdbDebugOutput::Release(THIS) +STDMETHODIMP_(ULONG) CdbDebugOutputBase::Release(THIS) { // This class is designed to be static so // there's no true refcount. return 0; } -STDMETHODIMP CdbDebugOutput::Output( +STDMETHODIMP CdbDebugOutputBase::Output( THIS_ IN ULONG mask, - IN PCSTR text + IN PCWSTR text ) { - UNREFERENCED_PARAMETER(mask); - m_pEngine->m_d->handleDebugOutput(text); + output(mask, QString::fromUtf16(text)); return S_OK; } +IDebugOutputCallbacksWide *CdbDebugOutputBase::getOutputCallback(IDebugClient5 *client) +{ + IDebugOutputCallbacksWide *rc; + if (FAILED(client->GetOutputCallbacksWide(&rc))) + return 0; + return rc; +} + +// ------------------------- CdbDebugOutput + +// Return a prefix for debugger messages +static QString prefix(ULONG mask) +{ + if (mask & (DEBUG_OUTPUT_PROMPT_REGISTERS)) { + static const QString p = QLatin1String("registers:"); + return p; + } + if (mask & (DEBUG_OUTPUT_EXTENSION_WARNING|DEBUG_OUTPUT_WARNING)) { + static const QString p = QLatin1String("warning:"); + return p; + } + if (mask & (DEBUG_OUTPUT_ERROR)) { + static const QString p = QLatin1String("error:"); + return p; + } + if (mask & DEBUG_OUTPUT_SYMBOLS) { + static const QString p = QLatin1String("symbols:"); + return p; + } + static const QString commonPrefix = QLatin1String("cdb:"); + return commonPrefix; +} + +enum OutputKind { DebuggerOutput, DebuggerPromptOutput, DebuggeeOutput, DebuggeePromptOutput }; + +static inline OutputKind outputKind(ULONG mask) +{ + if (mask & DEBUG_OUTPUT_DEBUGGEE) + return DebuggeeOutput; + if (mask & DEBUG_OUTPUT_DEBUGGEE_PROMPT) + return DebuggeePromptOutput; + if (mask & DEBUG_OUTPUT_PROMPT) + return DebuggerPromptOutput; + return DebuggerOutput; +} + +CdbDebugOutput::CdbDebugOutput() +{ +} + +void CdbDebugOutput::output(ULONG mask, const QString &msg) +{ + if (debugCDB > 1) + qDebug() << Q_FUNC_INFO << "\n " << msg; + + switch (outputKind(mask)) { + case DebuggerOutput: + debuggerOutput(prefix(mask), msg); + break; + case DebuggerPromptOutput: + emit debuggerInputPrompt(prefix(mask), msg); + break; + case DebuggeeOutput: + emit debuggeeOutput(msg); + break; + case DebuggeePromptOutput: + emit debuggeeInputPrompt(msg); + break; + } +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbdebugoutput.h b/src/plugins/debugger/cdb/cdbdebugoutput.h index ea36fb6c42bd1fa1e60209b87ef8b18fade49807..f13fedac35e57f1fc17444361daffeef0966ad11 100644 --- a/src/plugins/debugger/cdb/cdbdebugoutput.h +++ b/src/plugins/debugger/cdb/cdbdebugoutput.h @@ -33,16 +33,17 @@ #include <windows.h> #include <inc/dbgeng.h> +#include <QtCore/QObject> + namespace Debugger { namespace Internal { -class CdbDebugEngine; +// CdbDebugOutputBase is a base class for output handlers +// that takes care of the Active X magic and conversion to QString. -class CdbDebugOutput : public IDebugOutputCallbacks +class CdbDebugOutputBase : public IDebugOutputCallbacksWide { public: - explicit CdbDebugOutput(CdbDebugEngine* engine); - // IUnknown. STDMETHOD(QueryInterface)( THIS_ @@ -60,11 +61,48 @@ public: STDMETHOD(Output)( THIS_ IN ULONG mask, - IN PCSTR text + IN PCWSTR text ); + // Helpers to retrieve the output callbacks IF + static IDebugOutputCallbacksWide *getOutputCallback(IDebugClient5 *client); + +protected: + CdbDebugOutputBase(); + virtual void output(ULONG mask, const QString &message) = 0; +}; + +// Standard CDB output handler +class CdbDebugOutput : public QObject, public CdbDebugOutputBase +{ + Q_OBJECT +public: + CdbDebugOutput(); + +protected: + virtual void output(ULONG mask, const QString &message); + +signals: + void debuggerOutput(const QString &prefix, const QString &message); + void debuggerInputPrompt(const QString &prefix, const QString &message); + void debuggeeOutput(const QString &message); + void debuggeeInputPrompt(const QString &message); +}; + +// An output handler that adds lines to a string (to be +// used for cases in which linebreaks occur in-between calls +// to output). +class StringOutputHandler : public CdbDebugOutputBase +{ +public: + StringOutputHandler() {} + QString result() const { return m_result; } + +protected: + virtual void output(ULONG, const QString &message) { m_result += message; } + private: - CdbDebugEngine* m_pEngine; + QString m_result; }; } // namespace Internal diff --git a/src/plugins/debugger/cdb/cdbmodules.cpp b/src/plugins/debugger/cdb/cdbmodules.cpp new file mode 100644 index 0000000000000000000000000000000000000000..237ead9331226b1b9848f0f1e9da07295fc5c1df --- /dev/null +++ b/src/plugins/debugger/cdb/cdbmodules.cpp @@ -0,0 +1,163 @@ +/************************************************************************** +** +** 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 "cdbmodules.h" +#include "moduleshandler.h" +#include "cdbdebugengine_p.h" + +#include <QtCore/QFileInfo> + +namespace Debugger { +namespace Internal { + +bool getModuleList(IDebugSymbols3 *syms, QList<Module> *modules, QString *errorMessage) +{ + modules->clear(); + ULONG loadedCount, unloadedCount; + HRESULT hr = syms->GetNumberModules(&loadedCount, &unloadedCount); + if (FAILED(hr)) { + *errorMessage= msgComFailed("GetNumberModules", hr); + return false; + } + // retrieve array of parameters + const ULONG count = loadedCount + unloadedCount; + QVector<DEBUG_MODULE_PARAMETERS> parameters(count); + DEBUG_MODULE_PARAMETERS *parmPtr = &(*parameters.begin()); + memset(parmPtr, 0, sizeof(DEBUG_MODULE_PARAMETERS) * count); + hr = syms->GetModuleParameters(count, 0, 0u, parmPtr); + // E_INVALIDARG indicates 'Partial results' according to docu + if (FAILED(hr) && hr != E_INVALIDARG) { + *errorMessage= msgComFailed("GetModuleParameters", hr); + return false; + } + // fill array + const QString hexPrefix = QLatin1String("0x"); + WCHAR wszBuf[MAX_PATH]; + for (ULONG m = 0; m < count; m++) { + const DEBUG_MODULE_PARAMETERS &p = parameters.at(m); + if (p.Base != DEBUG_INVALID_OFFSET) { // Partial results? + Module module; + module.symbolsRead = (p.Flags & DEBUG_MODULE_USER_MODE) + && (p.SymbolType != DEBUG_SYMTYPE_NONE); + module.startAddress = hexPrefix + QString::number(p.Base, 16); + module.endAddress = hexPrefix + QString::number((p.Base + p.Size), 16); + hr = syms ->GetModuleNameStringWide(DEBUG_MODNAME_IMAGE, m, 0, wszBuf, MAX_PATH - 1, 0); + if (FAILED(hr) && hr != E_INVALIDARG) { + *errorMessage= msgComFailed("GetModuleNameStringWide", hr); + return false; + } + module.moduleName = QString::fromUtf16(wszBuf); + modules->push_back(module); + } + } + return true; +} + +// Search symbols matching a pattern +bool searchSymbols(IDebugSymbols3 *syms, const QString &pattern, + QStringList *matches, QString *errorMessage) +{ + matches->clear(); + ULONG64 handle = 0; + // E_NOINTERFACE means "no match". Apparently, it does not always + // set handle. + HRESULT hr = syms->StartSymbolMatchWide(pattern.utf16(), &handle); + if (hr == E_NOINTERFACE) { + if (handle) + syms->EndSymbolMatch(handle); + return true; + } + if (FAILED(hr)) { + *errorMessage= msgComFailed("StartSymbolMatchWide", hr); + return false; + } + WCHAR wszBuf[MAX_PATH]; + while (true) { + hr = syms->GetNextSymbolMatchWide(handle, wszBuf, MAX_PATH - 1, 0, 0); + if (hr == E_NOINTERFACE) + break; + if (hr == S_OK) + matches->push_back(QString::fromUtf16(wszBuf)); + } + syms->EndSymbolMatch(handle); + if (matches->empty()) + *errorMessage = QString::fromLatin1("No symbol matches '%1'.").arg(pattern); + return true; +} + +// Add missing the module specifier: "main" -> "project!main" + +ResolveSymbolResult resolveSymbol(IDebugSymbols3 *syms, QString *symbol, + QString *errorMessage) +{ + // Is it an incomplete symbol? + if (symbol->contains(QLatin1Char('!'))) + return ResolveSymbolOk; + // 'main' is a #define for gdb, but not for VS + if (*symbol == QLatin1String("qMain")) + *symbol = QLatin1String("main"); + // resolve + QStringList matches; + if (!searchSymbols(syms, *symbol, &matches, errorMessage)) + return ResolveSymbolError; + if (matches.empty()) + return ResolveSymbolNotFound; + *symbol = matches.front(); + if (matches.size() > 1) { + *errorMessage = QString::fromLatin1("Ambiguous symbol '%1': %2"). + arg(*symbol, matches.join(QString(QLatin1Char(' ')))); + return ResolveSymbolAmbiguous; + } + return ResolveSymbolOk; +} + +// List symbols of a module +bool getModuleSymbols(IDebugSymbols3 *syms, const QString &moduleName, + QList<Symbol> *symbols, QString *errorMessage) +{ + // Search all symbols and retrieve addresses + symbols->clear(); + QStringList matches; + const QString pattern = QFileInfo(moduleName).baseName() + QLatin1String("!*"); + if (!searchSymbols(syms, pattern, &matches, errorMessage)) + return false; + const QString hexPrefix = QLatin1String("0x"); + foreach (const QString &name, matches) { + Symbol symbol; + symbol.name = name; + ULONG64 offset = 0; + if (SUCCEEDED(syms->GetOffsetByNameWide(name.utf16(), &offset))) + symbol.address = hexPrefix + QString::number(offset, 16); + symbols->push_back(symbol); + } + return true; +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbmodules.h b/src/plugins/debugger/cdb/cdbmodules.h new file mode 100644 index 0000000000000000000000000000000000000000..9a375cdb031bf1465ddfe64bdec745693c7dc026 --- /dev/null +++ b/src/plugins/debugger/cdb/cdbmodules.h @@ -0,0 +1,65 @@ +/************************************************************************** +** +** 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 CDBMODULES_H +#define CDBMODULES_H + +#include <QtCore/QList> +#include <QtCore/QString> + +#include <windows.h> +#include <inc/dbgeng.h> + +namespace Debugger { +namespace Internal { + +class Module; +class Symbol; + +bool getModuleList(IDebugSymbols3 *syms, QList<Module> *modules, QString *errorMessage); +// Search symbols matching a pattern +bool searchSymbols(IDebugSymbols3 *syms, const QString &pattern, + QStringList *matches, QString *errorMessage); + +// ResolveSymbol: For symbols that are missing the module specifier, +// find the module and expand: "main" -> "project!main". + +enum ResolveSymbolResult { ResolveSymbolOk, ResolveSymbolAmbiguous, + ResolveSymbolNotFound, ResolveSymbolError }; + +ResolveSymbolResult resolveSymbol(IDebugSymbols3 *syms, QString *symbol, QString *errorMessage); + +// List symbols of a module +bool getModuleSymbols(IDebugSymbols3 *syms, const QString &moduleName, + QList<Symbol> *symbols, QString *errorMessage); + +} // namespace Internal +} // namespace Debugger + +#endif // CDBMODULES_H diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp index 0500657dc44b12f7d05cb996a97a8cb46b4113fe..07bf81c5bd47fd4e3eda673b3b8f2d3423c8223d 100644 --- a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp +++ b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp @@ -40,7 +40,8 @@ namespace Internal { CdbStackTraceContext::CdbStackTraceContext(IDebugSystemObjects4* pDebugSystemObjects, IDebugSymbols3* pDebugSymbols) : m_pDebugSystemObjects(pDebugSystemObjects), - m_pDebugSymbols(pDebugSymbols) + m_pDebugSymbols(pDebugSymbols), + m_instructionOffset(0) { } @@ -95,6 +96,8 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa for (ULONG i=0; i < frameCount; ++i) { StackFrame frame(i); const ULONG64 instructionOffset = m_cdbFrames[i].InstructionOffset; + if (i == 0) + m_instructionOffset = instructionOffset; frame.address = QString::fromLatin1("0x%1").arg(instructionOffset, 0, 16); m_pDebugSymbols->GetNameByOffsetWide(instructionOffset, wszBuf, MAX_PATH, 0, 0); @@ -163,5 +166,5 @@ IDebugSymbolGroup2 *CdbStackTraceContext::createSymbolGroup(int index, QString * return sg; } -} -} +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.h b/src/plugins/debugger/cdb/cdbstacktracecontext.h index bb8286859ef2ed3ade602808f5729a1be37a3511..efbd1c1e816cfd78c5b67cd5d858f0efb3281bd6 100644 --- a/src/plugins/debugger/cdb/cdbstacktracecontext.h +++ b/src/plugins/debugger/cdb/cdbstacktracecontext.h @@ -66,6 +66,9 @@ public: QList<StackFrame> frames() const { return m_frames; } inline int frameCount() const { return m_frames.size(); } + // Top-Level instruction offset for disassembler + ULONG64 instructionOffset() const { return m_instructionOffset; } + CdbSymbolGroupContext *symbolGroupContextAt(int index, QString *errorMessage); private: @@ -78,9 +81,10 @@ private: DEBUG_STACK_FRAME m_cdbFrames[maxFrames]; QVector <CdbSymbolGroupContext*> m_symbolContexts; QList<StackFrame> m_frames; + ULONG64 m_instructionOffset; }; -} -} +} // namespace Internal +} // namespace Debugger #endif // CDBSTACKTRACECONTEXT_H diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp index e381a5aa86d3ce544040e2b110e615eca2476de6..5f23419278544f27327afa920406843f4ac088be 100644 --- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp +++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp @@ -58,13 +58,13 @@ static inline void debugSymbolFlags(unsigned long f, QTextStream &str) str << "|DEBUG_SYMBOL_IS_LOCAL"; } - QTextStream &operator<<(QTextStream &str, const DEBUG_SYMBOL_PARAMETERS& p) +QTextStream &operator<<(QTextStream &str, const DEBUG_SYMBOL_PARAMETERS &p) { str << " Type=" << p.TypeId << " parent="; if (isTopLevelSymbol(p)) { str << "<ROOT>"; } else { - str << p.ParentSymbol; + str << p.ParentSymbol; } str << " Subs=" << p.SubElements << " flags=" << p.Flags << '/'; debugSymbolFlags(p.Flags, str); @@ -403,45 +403,67 @@ bool CdbSymbolGroupContext::assignValue(const QString &iname, const QString &val // format an array of integers as "0x323, 0x2322, ..." template <class Integer> -static QString hexFormatArrayHelper(const Integer *array, int size) +static QString formatArrayHelper(const Integer *array, int size, int base = 10) { QString rc; const QString hexPrefix = QLatin1String("0x"); const QString separator= QLatin1String(", "); + const bool hex = base == 16; for (int i = 0; i < size; i++) { if (i) rc += separator; - rc += hexPrefix; - rc += QString::number(array[i], 16); + if (hex) + rc += hexPrefix; + rc += QString::number(array[i], base); } return rc; } QString CdbSymbolGroupContext::hexFormatArray(const unsigned short *array, int size) { - return hexFormatArrayHelper(array, size); + return formatArrayHelper(array, size, 16); } -QString CdbSymbolGroupContext::debugValueToString(const DEBUG_VALUE &dv, IDebugControl4 *ctl, QString *type) +// Helper to format an integer with +// a hex prefix in case base = 16 +template <class Integer> + inline QString formatInteger(Integer value, int base) +{ + QString rc; + if (base == 16) + rc = QLatin1String("0x"); + rc += QString::number(value, base); + return rc; +} + +QString CdbSymbolGroupContext::debugValueToString(const DEBUG_VALUE &dv, IDebugControl4 *ctl, + QString *qType, + int integerBase) { switch (dv.Type) { case DEBUG_VALUE_INT8: - *type = QLatin1String("char"); - return QString::number(dv.I8); + if (qType) + *qType = QLatin1String("char"); + return formatInteger(dv.I8, integerBase); case DEBUG_VALUE_INT16: - *type = QLatin1String("short"); - return QString::number(static_cast<short>(dv.I16)); + if (qType) + *qType = QLatin1String("short"); + return formatInteger(static_cast<short>(dv.I16), integerBase); case DEBUG_VALUE_INT32: - *type = QLatin1String("long"); - return QString::number(static_cast<long>(dv.I32)); + if (qType) + *qType = QLatin1String("long"); + return formatInteger(static_cast<long>(dv.I32), integerBase); case DEBUG_VALUE_INT64: - *type = QLatin1String("long long"); - return QString::number(static_cast<long long>(dv.I64)); + if (qType) + *qType = QLatin1String("long long"); + return formatInteger(static_cast<long long>(dv.I64), integerBase); case DEBUG_VALUE_FLOAT32: - *type = QLatin1String("float"); + if (qType) + *qType = QLatin1String("float"); return QString::number(dv.F32); case DEBUG_VALUE_FLOAT64: - *type = QLatin1String("double"); + if (qType) + *qType = QLatin1String("double"); return QString::number(dv.F64); case DEBUG_VALUE_FLOAT80: case DEBUG_VALUE_FLOAT128: { // Convert to double @@ -449,28 +471,32 @@ QString CdbSymbolGroupContext::debugValueToString(const DEBUG_VALUE &dv, IDebugC double d = 0.0; if (SUCCEEDED(ctl->CoerceValue(const_cast<DEBUG_VALUE*>(&dv), DEBUG_VALUE_FLOAT64, &doubleValue))) d = dv.F64; - *type = dv.Type == DEBUG_VALUE_FLOAT80 ? QLatin1String("80bit-float") : QLatin1String("128bit-float"); + if (qType) + *qType = QLatin1String(dv.Type == DEBUG_VALUE_FLOAT80 ? "80bit-float" : "128bit-float"); return QString::number(d); } case DEBUG_VALUE_VECTOR64: { - *type = QLatin1String("64bit-vector"); - QString rc = QLatin1String("bytes: "); - rc += hexFormatArrayHelper(dv.VI8, 8); - rc += QLatin1String(" long: "); - rc += hexFormatArrayHelper(dv.VI32, 2); - return rc; - } + if (qType) + *qType = QLatin1String("64bit-vector"); + QString rc = QLatin1String("bytes: "); + rc += formatArrayHelper(dv.VI8, 8, integerBase); + rc += QLatin1String(" long: "); + rc += formatArrayHelper(dv.VI32, 2, integerBase); + return rc; + } case DEBUG_VALUE_VECTOR128: { - *type = QLatin1String("128bit-vector"); - QString rc = QLatin1String("bytes: "); - rc += hexFormatArrayHelper(dv.VI8, 16); - rc += QLatin1String(" long long: "); - rc += hexFormatArrayHelper(dv.VI64, 2); - return rc; - } + if (qType) + *qType = QLatin1String("128bit-vector"); + QString rc = QLatin1String("bytes: "); + rc += formatArrayHelper(dv.VI8, 16, integerBase); + rc += QLatin1String(" long long: "); + rc += formatArrayHelper(dv.VI64, 2, integerBase); + return rc; + } } - *type = QString::fromLatin1("Unknown type #%1:").arg(dv.Type); - return hexFormatArrayHelper(dv.RawBytes, 24); + if (qType) + *qType = QString::fromLatin1("Unknown type #%1:").arg(dv.Type); + return formatArrayHelper(dv.RawBytes, 24, integerBase); } // - Watch model functions @@ -613,5 +639,5 @@ bool CdbSymbolGroupContext::completeModel(CdbSymbolGroupContext *sg, return true; } -} -} +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h index 46254ce4e04b41b087f038934cdb824d2117a836..0773133c18aa2a0eb962d1b57bb5e40f36cc355a 100644 --- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h +++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h @@ -41,7 +41,7 @@ #include <QtCore/QMap> namespace Debugger { - namespace Internal { +namespace Internal { class WatchData; class WatchHandler; @@ -92,7 +92,7 @@ public: inline bool isExpanded(const QString &prefix) const { return symbolState(prefix) == ExpandedSymbol; } // Helper to convert a DEBUG_VALUE structure to a string representation - static QString debugValueToString(const DEBUG_VALUE &dv, IDebugControl4 *ctl, QString *type); + static QString debugValueToString(const DEBUG_VALUE &dv, IDebugControl4 *ctl, QString *type = 0, int integerBase = 10); // format an array of unsigned longs as "0x323, 0x2322, ..." static QString hexFormatArray(const unsigned short *array, int size); @@ -145,6 +145,7 @@ bool CdbSymbolGroupContext::getChildSymbols(const QString &prefix, OutputIterato return true; } -} -} +} // namespace Internal +} // namespace Debugger + #endif // CDBSYMBOLGROUPCONTEXT_H diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp index a2e2b6f06146be8cc98db6c69d9aedcaa640b89c..6ade9b34fde2652d6e21e9207c04529ea66fdc4e 100644 --- a/src/plugins/debugger/debuggeractions.cpp +++ b/src/plugins/debugger/debuggeractions.cpp @@ -54,7 +54,7 @@ namespace Internal { ////////////////////////////////////////////////////////////////////////// DebuggerSettings::DebuggerSettings(QObject *parent) - : QObject(parent) + : QObject(parent), m_registerFormatGroup(0) {} DebuggerSettings::~DebuggerSettings() @@ -74,25 +74,25 @@ void DebuggerSettings::readSettings(QSettings *settings) item->readSettings(settings); } -void DebuggerSettings::writeSettings(QSettings *settings) +void DebuggerSettings::writeSettings(QSettings *settings) const { foreach (SavedAction *item, m_items) item->writeSettings(settings); } -SavedAction *DebuggerSettings::item(int code) +SavedAction *DebuggerSettings::item(int code) const { QTC_ASSERT(m_items.value(code, 0), return 0); return m_items.value(code, 0); } -QString DebuggerSettings::dump() +QString DebuggerSettings::dump() const { QString out; QTextStream ts(&out); ts << "Debugger settings: "; foreach (SavedAction *item, m_items) - ts << "\n" << item->value().toString(); + ts << '\n' << item->value().toString(); return out; } @@ -153,27 +153,27 @@ DebuggerSettings *DebuggerSettings::instance() // // DebuggingHelper - // + const QString debugModeGroup = QLatin1String("DebugMode"); item = new SavedAction(instance); instance->insertItem(UseDebuggingHelpers, item); item->setDefaultValue(true); - item->setSettingsKey("DebugMode", "UseDebuggingHelper"); + item->setSettingsKey(debugModeGroup, QLatin1String("UseDebuggingHelper")); item->setText(tr("Use Debugging Helper")); item->setCheckable(true); item->setDefaultValue(true); item = new SavedAction(instance); instance->insertItem(UseCustomDebuggingHelperLocation, item); - item->setSettingsKey("DebugMode", "CustomDebuggingHelperLocation"); + item->setSettingsKey(debugModeGroup, QLatin1String("CustomDebuggingHelperLocation")); item->setCheckable(true); item = new SavedAction(instance); instance->insertItem(CustomDebuggingHelperLocation, item); - item->setSettingsKey("DebugMode", "CustomDebuggingHelperLocation"); + item->setSettingsKey(debugModeGroup, QLatin1String("CustomDebuggingHelperLocation")); item = new SavedAction(instance); instance->insertItem(DebugDebuggingHelpers, item); - item->setSettingsKey("DebugMode", "DebugDebuggingHelpers"); + item->setSettingsKey(debugModeGroup, QLatin1String("DebugDebuggingHelpers")); item->setText(tr("Debug debugging helper")); item->setCheckable(true); @@ -193,115 +193,120 @@ DebuggerSettings *DebuggerSettings::instance() // Registers // - QActionGroup *registerFormatGroup = new QActionGroup(instance); - registerFormatGroup->setExclusive(true); + instance->m_registerFormatGroup = new QActionGroup(instance); + instance->m_registerFormatGroup->setExclusive(true); item = new SavedAction(instance); item->setText(tr("Hexadecimal")); item->setCheckable(true); - item->setSettingsKey("DebugMode", "FormatHexadecimal"); + item->setSettingsKey(debugModeGroup, QLatin1String("FormatHexadecimal")); item->setChecked(true); + item->setData(FormatHexadecimal); instance->insertItem(FormatHexadecimal, item); - registerFormatGroup->addAction(item); + instance->m_registerFormatGroup->addAction(item); item = new SavedAction(instance); item->setText(tr("Decimal")); item->setCheckable(true); - item->setSettingsKey("DebugMode", "FormatDecimal"); + item->setSettingsKey(debugModeGroup, QLatin1String("FormatDecimal")); + item->setData(FormatDecimal); instance->insertItem(FormatDecimal, item); - registerFormatGroup->addAction(item); + instance->m_registerFormatGroup->addAction(item); item = new SavedAction(instance); item->setText(tr("Octal")); item->setCheckable(true); - item->setSettingsKey("DebugMode", "FormatOctal"); + item->setSettingsKey(debugModeGroup, QLatin1String("FormatOctal")); + item->setData(FormatOctal); instance->insertItem(FormatOctal, item); - registerFormatGroup->addAction(item); + instance->m_registerFormatGroup->addAction(item); item = new SavedAction(instance); item->setText(tr("Binary")); item->setCheckable(true); - item->setSettingsKey("DebugMode", "FormatBinary"); + item->setSettingsKey(debugModeGroup, QLatin1String("FormatBinary")); + item->setData(FormatBinary); instance->insertItem(FormatBinary, item); - registerFormatGroup->addAction(item); + instance->m_registerFormatGroup->addAction(item); item = new SavedAction(instance); item->setText(tr("Raw")); item->setCheckable(true); - item->setSettingsKey("DebugMode", "FormatRaw"); + item->setSettingsKey(debugModeGroup, QLatin1String("FormatRaw")); instance->insertItem(FormatRaw, item); - registerFormatGroup->addAction(item); + item->setData(FormatRaw); + instance->m_registerFormatGroup->addAction(item); item = new SavedAction(instance); item->setText(tr("Natural")); item->setCheckable(true); - item->setSettingsKey("DebugMode", "FormatNatural"); + item->setSettingsKey(debugModeGroup, QLatin1String("FormatNatural")); + item->setData(FormatNatural); instance->insertItem(FormatNatural, item); - registerFormatGroup->addAction(item); - + instance->m_registerFormatGroup->addAction(item); // // Settings // item = new SavedAction(instance); - item->setSettingsKey("DebugMode", "Location"); + item->setSettingsKey(debugModeGroup, QLatin1String("Location")); instance->insertItem(GdbLocation, item); item = new SavedAction(instance); - item->setSettingsKey("DebugMode", "Environment"); + item->setSettingsKey(debugModeGroup, QLatin1String("Environment")); instance->insertItem(GdbEnvironment, item); item = new SavedAction(instance); - item->setSettingsKey("DebugMode", "ScriptFile"); + item->setSettingsKey(debugModeGroup, QLatin1String("ScriptFile")); instance->insertItem(GdbScriptFile, item); item = new SavedAction(instance); - item->setSettingsKey("DebugMode", "AutoQuit"); + item->setSettingsKey(debugModeGroup, QLatin1String("AutoQuit")); item->setText(tr("Automatically quit debugger")); item->setCheckable(true); instance->insertItem(AutoQuit, item); item = new SavedAction(instance); - item->setSettingsKey("DebugMode", "UseToolTips"); + item->setSettingsKey(debugModeGroup, QLatin1String("UseToolTips")); item->setText(tr("Use tooltips when debugging")); item->setCheckable(true); instance->insertItem(UseToolTips, item); item = new SavedAction(instance); - item->setDefaultValue("xterm"); - item->setSettingsKey("DebugMode", "Terminal"); + item->setDefaultValue(QLatin1String("xterm")); + item->setSettingsKey(debugModeGroup, QLatin1String("Terminal")); instance->insertItem(TerminalApplication, item); item = new SavedAction(instance); - item->setSettingsKey("DebugMode", "ListSourceFiles"); + item->setSettingsKey(debugModeGroup, QLatin1String("ListSourceFiles")); item->setText(tr("List source files")); item->setCheckable(true); instance->insertItem(ListSourceFiles, item); item = new SavedAction(instance); - item->setSettingsKey("DebugMode", "SkipKnownFrames"); + item->setSettingsKey(debugModeGroup, QLatin1String("SkipKnownFrames")); item->setText(tr("Skip known frames")); item->setCheckable(true); instance->insertItem(SkipKnownFrames, item); item = new SavedAction(instance); - item->setSettingsKey("DebugMode", "AllPluginBreakpoints"); + item->setSettingsKey(debugModeGroup, QLatin1String("AllPluginBreakpoints")); instance->insertItem(AllPluginBreakpoints, item); item = new SavedAction(instance); - item->setSettingsKey("DebugMode", "SelectedPluginBreakpoints"); + item->setSettingsKey(debugModeGroup, QLatin1String("SelectedPluginBreakpoints")); instance->insertItem(SelectedPluginBreakpoints, item); item = new SavedAction(instance); - item->setSettingsKey("DebugMode", "NoPluginBreakpoints"); + item->setSettingsKey(debugModeGroup, QLatin1String("NoPluginBreakpoints")); instance->insertItem(NoPluginBreakpoints, item); item = new SavedAction(instance); - item->setSettingsKey("DebugMode", "SelectedPluginBreakpointsPattern"); + item->setSettingsKey(debugModeGroup, QLatin1String("SelectedPluginBreakpointsPattern")); instance->insertItem(SelectedPluginBreakpointsPattern, item); item = new SavedAction(instance); - item->setSettingsKey("DebugMode", "MaximalStackDepth"); + item->setSettingsKey(debugModeGroup, QLatin1String("MaximalStackDepth")); item->setDefaultValue(20); instance->insertItem(MaximalStackDepth, item); @@ -316,6 +321,11 @@ DebuggerSettings *DebuggerSettings::instance() return instance; } +int DebuggerSettings::checkedRegisterFormatAction() const +{ + return m_registerFormatGroup->checkedAction()->data().toInt(); +} + ////////////////////////////////////////////////////////////////////////// // // DebuggerActions @@ -327,6 +337,11 @@ SavedAction *theDebuggerAction(int code) return DebuggerSettings::instance()->item(code); } +int checkedRegisterFormatAction() +{ + return DebuggerSettings::instance()->checkedRegisterFormatAction(); +} + bool theDebuggerBoolSetting(int code) { return DebuggerSettings::instance()->item(code)->value().toBool(); diff --git a/src/plugins/debugger/debuggeractions.h b/src/plugins/debugger/debuggeractions.h index 4b0ebd66c86f71b5331b6c720921c3421a482118..6fc9366f7bf6ce9dd733f0de28e549132b85f9e4 100644 --- a/src/plugins/debugger/debuggeractions.h +++ b/src/plugins/debugger/debuggeractions.h @@ -34,6 +34,9 @@ #include <utils/savedaction.h> +QT_BEGIN_NAMESPACE +class QActionGroup; +QT_END_NAMESPACE namespace Debugger { namespace Internal { @@ -46,18 +49,22 @@ public: ~DebuggerSettings(); void insertItem(int code, Core::Utils::SavedAction *item); - Core::Utils::SavedAction *item(int code); + Core::Utils::SavedAction *item(int code) const; - QString dump(); + QString dump() const; static DebuggerSettings *instance(); + // Return one of FormatHexadecimal, FormatDecimal,... + int checkedRegisterFormatAction() const; + public slots: void readSettings(QSettings *settings); - void writeSettings(QSettings *settings); + void writeSettings(QSettings *settings) const; private: QHash<int, Core::Utils::SavedAction *> m_items; + QActionGroup *m_registerFormatGroup; }; @@ -125,7 +132,10 @@ enum DebuggerActionCode // singleton access Core::Utils::SavedAction *theDebuggerAction(int code); -// convienience +// Return one of FormatHexadecimal, FormatDecimal,... +int checkedRegisterFormatAction(); + +// convenience bool theDebuggerBoolSetting(int code); QString theDebuggerStringSetting(int code); diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp index cf2066c7909ac6d7a320e771dbc6a4e976218b18..5f8f07aed0ec993e4049ddf7d2e5f763a630f2eb 100644 --- a/src/plugins/debugger/debuggermanager.cpp +++ b/src/plugins/debugger/debuggermanager.cpp @@ -175,7 +175,7 @@ void DebuggerManager::init() m_statusLabel = new QLabel; m_breakWindow = new BreakWindow; m_disassemblerWindow = new DisassemblerWindow; - m_modulesWindow = new ModulesWindow; + m_modulesWindow = new ModulesWindow(this); m_outputWindow = new DebuggerOutputWindow; m_registerWindow = new RegisterWindow; m_stackWindow = new StackWindow; @@ -213,6 +213,8 @@ void DebuggerManager::init() QAbstractItemView *disassemblerView = qobject_cast<QAbstractItemView *>(m_disassemblerWindow); disassemblerView->setModel(m_disassemblerHandler->model()); + connect(m_disassemblerWindow, SIGNAL(reloadDisassemblerRequested()), + this, SLOT(reloadDisassembler())); // Breakpoints m_breakHandler = new BreakHandler; @@ -996,6 +998,12 @@ void DebuggerManager::loadSymbols(const QString &module) m_engine->loadSymbols(module); } +QList<Symbol> DebuggerManager::moduleSymbols(const QString &moduleName) +{ + QTC_ASSERT(m_engine, return QList<Symbol>()); + return m_engine->moduleSymbols(moduleName); +} + void DebuggerManager::stepExec() { QTC_ASSERT(m_engine, return); diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h index 72c1467c52efe4243c46dc8f40bc0bbe018f23dc..b9709a68fa59f1fe498b8af4b191b2722c81b53c 100644 --- a/src/plugins/debugger/debuggermanager.h +++ b/src/plugins/debugger/debuggermanager.h @@ -65,7 +65,7 @@ class WatchHandler; class SourceFilesWindow; class WatchData; class BreakpointData; - +class Symbol; // Note: the Debugger process itself is referred to as 'Debugger', // whereas the debugged process is referred to as 'Inferior' or 'Debuggee'. @@ -309,6 +309,8 @@ public: int status() const { return m_status; } DebuggerStartMode startMode() const { return m_startMode; } + QList<Symbol> moduleSymbols(const QString &moduleName); + signals: void debuggingFinished(); void inferiorPidChanged(qint64 pid); diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index ea41b9854eae2b38c0c852d0a49e5b2a199363d9..8a1cb6baca77baba3ed1206640ec2c2c11c5a9d9 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -650,6 +650,19 @@ bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMess connect(resetToSimpleAction, SIGNAL(triggered()), m_manager, SLOT(setSimpleDockWidgetArrangement())); + connect(theDebuggerAction(FormatHexadecimal), SIGNAL(triggered()), + m_manager, SLOT(reloadRegisters())); + connect(theDebuggerAction(FormatDecimal), SIGNAL(triggered()), + m_manager, SLOT(reloadRegisters())); + connect(theDebuggerAction(FormatOctal), SIGNAL(triggered()), + m_manager, SLOT(reloadRegisters())); + connect(theDebuggerAction(FormatBinary), SIGNAL(triggered()), + m_manager, SLOT(reloadRegisters())); + connect(theDebuggerAction(FormatRaw), SIGNAL(triggered()), + m_manager, SLOT(reloadRegisters())); + connect(theDebuggerAction(FormatNatural), SIGNAL(triggered()), + m_manager, SLOT(reloadRegisters())); + // FIXME: m_generalOptionPage = new GdbOptionPage; addObject(m_generalOptionPage); diff --git a/src/plugins/debugger/disassemblerhandler.cpp b/src/plugins/debugger/disassemblerhandler.cpp index 9ef9efec9c636bba22cc46ecc407537b7f204e5d..ee03f6cfd55bf1eade7bfe6707b35e47f1822638 100644 --- a/src/plugins/debugger/disassemblerhandler.cpp +++ b/src/plugins/debugger/disassemblerhandler.cpp @@ -39,6 +39,14 @@ using namespace Debugger; using namespace Debugger::Internal; +void DisassemblerLine::clear() +{ + address.clear(); + symbol.clear(); + addressDisplay.clear(); + symbolDisplay.clear(); + mnemonic.clear(); +} ////////////////////////////////////////////////////////////////// // diff --git a/src/plugins/debugger/disassemblerhandler.h b/src/plugins/debugger/disassemblerhandler.h index 6c0a5b37badef8febf48f9a02e85f5d41169f44e..4b73e69ddbfd4e4c92411c9d36a2e75a6c62df4d 100644 --- a/src/plugins/debugger/disassemblerhandler.h +++ b/src/plugins/debugger/disassemblerhandler.h @@ -41,6 +41,8 @@ namespace Internal { class DisassemblerLine { public: + void clear(); + QString address; QString symbol; QString addressDisplay; diff --git a/src/plugins/debugger/gdbengine.cpp b/src/plugins/debugger/gdbengine.cpp index 906b38a72b1fb8398b381b3ab16e486b5fd794c8..0279c15181f724f4559f09a68dbe444fd222f52d 100644 --- a/src/plugins/debugger/gdbengine.cpp +++ b/src/plugins/debugger/gdbengine.cpp @@ -221,19 +221,6 @@ void GdbEngine::initializeConnections() connect(theDebuggerAction(RecheckDebuggingHelpers), SIGNAL(triggered()), this, SLOT(recheckDebuggingHelperAvailability())); - connect(theDebuggerAction(FormatHexadecimal), SIGNAL(triggered()), - this, SLOT(reloadRegisters())); - connect(theDebuggerAction(FormatDecimal), SIGNAL(triggered()), - this, SLOT(reloadRegisters())); - connect(theDebuggerAction(FormatOctal), SIGNAL(triggered()), - this, SLOT(reloadRegisters())); - connect(theDebuggerAction(FormatBinary), SIGNAL(triggered()), - this, SLOT(reloadRegisters())); - connect(theDebuggerAction(FormatRaw), SIGNAL(triggered()), - this, SLOT(reloadRegisters())); - connect(theDebuggerAction(FormatNatural), SIGNAL(triggered()), - this, SLOT(reloadRegisters())); - connect(theDebuggerAction(ExpandStack), SIGNAL(triggered()), this, SLOT(reloadFullStack())); connect(theDebuggerAction(MaximalStackDepth), SIGNAL(triggered()), @@ -2402,6 +2389,40 @@ void GdbEngine::loadAllSymbols() reloadModules(); } +QList<Symbol> GdbEngine::moduleSymbols(const QString &moduleName) +{ + QList<Symbol> rc; + bool success = false; + QString errorMessage; + do { + const QString nmBinary = QLatin1String("nm"); + QProcess proc; + proc.start(nmBinary, QStringList() << QLatin1String("-D") << moduleName); + if (!proc.waitForFinished()) { + errorMessage = tr("Unable to run '%1': %2").arg(nmBinary, proc.errorString()); + break; + } + const QString contents = QString::fromLocal8Bit(proc.readAllStandardOutput()); + const QRegExp re(QLatin1String("([0-9a-f]+)?\\s+([^\\s]+)\\s+([^\\s]+)")); + Q_ASSERT(re.isValid()); + foreach (const QString &line, contents.split(QLatin1Char('\n'))) { + if (re.indexIn(line) != -1) { + Symbol symbol; + symbol.address = re.cap(1); + symbol.state = re.cap(2); + symbol.name = re.cap(3); + rc.push_back(symbol); + } else { + qWarning("moduleSymbols: unhandled: %s", qPrintable(line)); + } + } + success = true; + } while (false); + if (!success) + qWarning("moduleSymbols: %s\n", qPrintable(errorMessage)); + return rc; +} + void GdbEngine::reloadModules() { sendCommand("info shared", ModulesList, QVariant()); @@ -2619,22 +2640,28 @@ void GdbEngine::handleStackListThreads(const GdbResultRecord &record, int id) // ////////////////////////////////////////////////////////////////////// +static inline char registerFormatChar() +{ + switch(checkedRegisterFormatAction()) { + case FormatHexadecimal: + return 'x'; + case FormatDecimal: + return 'd'; + case FormatOctal: + return 'o'; + case FormatBinary: + return 't'; + case FormatRaw: + return 'r'; + default: + break; + } + return 'N'; +} + void GdbEngine::reloadRegisters() { - QString format; - if (theDebuggerAction(FormatHexadecimal)->isChecked()) - format = "x"; - else if (theDebuggerAction(FormatDecimal)->isChecked()) - format = "d"; - else if (theDebuggerAction(FormatOctal)->isChecked()) - format = "o"; - else if (theDebuggerAction(FormatBinary)->isChecked()) - format = "t"; - else if (theDebuggerAction(FormatRaw)->isChecked()) - format = "r"; - else - format = "N"; - sendCommand("-data-list-register-values " + format, RegisterListValues); + sendCommand(QLatin1String("-data-list-register-values ") + QLatin1Char(registerFormatChar()), RegisterListValues); } void GdbEngine::handleRegisterListNames(const GdbResultRecord &record) diff --git a/src/plugins/debugger/gdbengine.h b/src/plugins/debugger/gdbengine.h index aebb5da6373ad95f508c4a944fe4adf162330988..b5f5868dcf57566244280517690f2d8c2a82395f 100644 --- a/src/plugins/debugger/gdbengine.h +++ b/src/plugins/debugger/gdbengine.h @@ -129,6 +129,7 @@ private: void loadSymbols(const QString &moduleName); void loadAllSymbols(); + virtual QList<Symbol> moduleSymbols(const QString &moduleName); Q_SLOT void setDebugDebuggingHelpers(const QVariant &on); Q_SLOT void setUseDebuggingHelpers(const QVariant &on); diff --git a/src/plugins/debugger/idebuggerengine.h b/src/plugins/debugger/idebuggerengine.h index 2bb44f8e47bd851695d0a844c90ebd97d3e74a61..0d3863a34e71eec3a2bf1b01bcbfaa292e56cdaa 100644 --- a/src/plugins/debugger/idebuggerengine.h +++ b/src/plugins/debugger/idebuggerengine.h @@ -31,6 +31,7 @@ #define DEBUGGER_IDEBUGGERENGINE_H #include <QtCore/QObject> +#include <QtCore/QList> QT_BEGIN_NAMESPACE class QPoint; @@ -40,6 +41,8 @@ QT_END_NAMESPACE namespace Debugger { namespace Internal { +class Symbol; + class IDebuggerEngine : public QObject { public: @@ -79,6 +82,7 @@ public: virtual void reloadModules() = 0; virtual void loadSymbols(const QString &moduleName) = 0; virtual void loadAllSymbols() = 0; + virtual QList<Symbol> moduleSymbols(const QString &moduleName) = 0; virtual void reloadRegisters() = 0; diff --git a/src/plugins/debugger/moduleshandler.h b/src/plugins/debugger/moduleshandler.h index 4c555c83929da5db324aa84e3bf16e322952791f..a6d77e4a28eef7cb8bcb0cdd0149b04451ccc69e 100644 --- a/src/plugins/debugger/moduleshandler.h +++ b/src/plugins/debugger/moduleshandler.h @@ -51,6 +51,19 @@ enum ModulesModelRoles LoadAllSymbolsRole }; +////////////////////////////////////////////////////////////////// +// +// Symbol +// +////////////////////////////////////////////////////////////////// + +class Symbol +{ +public: + QString address; + QString state; + QString name; +}; ////////////////////////////////////////////////////////////////// // diff --git a/src/plugins/debugger/moduleswindow.cpp b/src/plugins/debugger/moduleswindow.cpp index 8d87709eadc0d08c8501f89894eeba85c8e82af9..6435e8b65a6ee8f01c7e841111d90397c523adeb 100644 --- a/src/plugins/debugger/moduleswindow.cpp +++ b/src/plugins/debugger/moduleswindow.cpp @@ -29,6 +29,7 @@ #include "moduleswindow.h" #include "moduleshandler.h" // for model roles +#include "debuggermanager.h" #include <QtCore/QDebug> #include <QtCore/QProcess> @@ -40,7 +41,7 @@ #include <QtGui/QResizeEvent> #include <QtGui/QToolButton> #include <QtGui/QTreeWidget> - +#include <QtGui/QApplication> /////////////////////////////////////////////////////////////////////////// // @@ -48,10 +49,14 @@ // /////////////////////////////////////////////////////////////////////////// -using Debugger::Internal::ModulesWindow; +namespace Debugger { +namespace Internal { -ModulesWindow::ModulesWindow(QWidget *parent) - : QTreeView(parent), m_alwaysResizeColumnsToContents(false) +ModulesWindow::ModulesWindow(DebuggerManager *debuggerManager, + QWidget *parent) : + QTreeView(parent), + m_alwaysResizeColumnsToContents(false), + m_debuggerManager(debuggerManager) { setWindowTitle(tr("Modules")); setSortingEnabled(true); @@ -88,9 +93,12 @@ void ModulesWindow::resizeEvent(QResizeEvent *event) void ModulesWindow::contextMenuEvent(QContextMenuEvent *ev) { + QString name; QModelIndex index = indexAt(ev->pos()); - index = index.sibling(index.row(), 0); - QString name = model()->data(index).toString(); + if (index.isValid()) + index = index.sibling(index.row(), 0); + if (index.isValid()) + name = model()->data(index).toString(); QMenu menu; QAction *act0 = new QAction(tr("Update module list"), &menu); @@ -116,9 +124,6 @@ void ModulesWindow::contextMenuEvent(QContextMenuEvent *ev) act5->setDisabled(name.isEmpty()); act6->setDisabled(name.isEmpty()); act7->setDisabled(name.isEmpty()); - #ifndef Q_OS_LINUX - act7->setDisabled(true); - #endif menu.addAction(act0); menu.addAction(act4); @@ -178,28 +183,26 @@ void ModulesWindow::showSymbols(const QString &name) { if (name.isEmpty()) return; - QProcess proc; - proc.start("nm", QStringList() << "-D" << name); - proc.waitForFinished(); + QApplication::setOverrideCursor(Qt::WaitCursor); + const QList<Symbol> symbols = m_debuggerManager->moduleSymbols(name); + QApplication::restoreOverrideCursor(); + if (symbols.empty()) + return; QTreeWidget *w = new QTreeWidget; w->setColumnCount(3); w->setRootIsDecorated(false); w->setAlternatingRowColors(true); - //w->header()->hide(); w->setHeaderLabels(QStringList() << tr("Address") << tr("Code") << tr("Symbol")); w->setWindowTitle(tr("Symbols in \"%1\"").arg(name)); - QString contents = QString::fromLocal8Bit(proc.readAllStandardOutput()); - QRegExp re("([0-9a-f]+)?\\s+([^\\s]+)\\s+([^\\s]+)"); - foreach (QString line, contents.split('\n')) { - if (re.indexIn(line) != -1) { - QTreeWidgetItem *it = new QTreeWidgetItem; - it->setData(0, Qt::DisplayRole, re.cap(1)); - it->setData(1, Qt::DisplayRole, re.cap(2)); - it->setData(2, Qt::DisplayRole, re.cap(3)); - w->addTopLevelItem(it); - } else { - qDebug() << "UNHANDLED LINE" << line; - } + foreach (const Symbol &s, symbols) { + QTreeWidgetItem *it = new QTreeWidgetItem; + it->setData(0, Qt::DisplayRole, s.address); + it->setData(1, Qt::DisplayRole, s.state); + it->setData(2, Qt::DisplayRole, s.name); + w->addTopLevelItem(it); } emit newDockRequested(w); } + +} +} diff --git a/src/plugins/debugger/moduleswindow.h b/src/plugins/debugger/moduleswindow.h index 36377fb58eeb657f5c742020391627a8be70ff0a..fc2b1af2acff4ee3f8a49fb340ad07823d7a3513 100644 --- a/src/plugins/debugger/moduleswindow.h +++ b/src/plugins/debugger/moduleswindow.h @@ -35,12 +35,14 @@ namespace Debugger { namespace Internal { +class DebuggerManager; + class ModulesWindow : public QTreeView { Q_OBJECT public: - explicit ModulesWindow(QWidget *parent = 0); + explicit ModulesWindow(DebuggerManager *debuggerManager, QWidget *parent = 0); signals: void reloadModulesRequested(); @@ -62,6 +64,7 @@ private: void setModel(QAbstractItemModel *model); bool m_alwaysResizeColumnsToContents; + DebuggerManager *m_debuggerManager; }; } // namespace Internal diff --git a/src/plugins/debugger/scriptengine.cpp b/src/plugins/debugger/scriptengine.cpp index 3ee8b7f8697b7a7a5e47e45d90340baef5637c2e..4ab74e2154a399fd2229a8c24e7fcfe08d5fc93e 100644 --- a/src/plugins/debugger/scriptengine.cpp +++ b/src/plugins/debugger/scriptengine.cpp @@ -38,6 +38,7 @@ #include "registerhandler.h" #include "stackhandler.h" #include "watchhandler.h" +#include "moduleshandler.h" #include <utils/qtcassert.h> @@ -405,6 +406,10 @@ void ScriptEngine::reloadModules() { } +QList<Symbol> ScriptEngine::moduleSymbols(const QString & /*moduleName*/) +{ + return QList<Symbol>(); +} ////////////////////////////////////////////////////////////////////// diff --git a/src/plugins/debugger/scriptengine.h b/src/plugins/debugger/scriptengine.h index 5e009327da7f63054ac4a551177cc59157385560..ee5612d09d84d3c59bf04f1849f36da2008d26c1 100644 --- a/src/plugins/debugger/scriptengine.h +++ b/src/plugins/debugger/scriptengine.h @@ -102,6 +102,7 @@ private: void loadSymbols(const QString &moduleName); void loadAllSymbols(); + virtual QList<Symbol> moduleSymbols(const QString &moduleName); void reloadDisassembler(); void reloadModules(); void reloadRegisters() {} diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp index f26d0ffcff491b143885be871cd3b1d25f338703..0b76cfc1de662a414fc571fc56880cba49d42593 100644 --- a/src/plugins/fakevim/fakevimhandler.cpp +++ b/src/plugins/fakevim/fakevimhandler.cpp @@ -96,15 +96,16 @@ namespace Internal { // /////////////////////////////////////////////////////////////////////// -#define StartOfLine QTextCursor::StartOfLine -#define EndOfLine QTextCursor::EndOfLine -#define MoveAnchor QTextCursor::MoveAnchor -#define KeepAnchor QTextCursor::KeepAnchor -#define Up QTextCursor::Up -#define Down QTextCursor::Down -#define Right QTextCursor::Right -#define Left QTextCursor::Left -#define EndOfDocument QTextCursor::End +#define StartOfLine QTextCursor::StartOfLine +#define EndOfLine QTextCursor::EndOfLine +#define MoveAnchor QTextCursor::MoveAnchor +#define KeepAnchor QTextCursor::KeepAnchor +#define Up QTextCursor::Up +#define Down QTextCursor::Down +#define Right QTextCursor::Right +#define Left QTextCursor::Left +#define EndOfDocument QTextCursor::End +#define StartOfDocument QTextCursor::Start #define EDITOR(s) (m_textedit ? m_textedit->s : m_plaintextedit->s) @@ -274,8 +275,6 @@ public: void moveToWordBoundary(bool simple, bool forward); // to reduce line noise - typedef QTextCursor::MoveOperation MoveOperation; - typedef QTextCursor::MoveMode MoveMode; void moveToEndOfDocument() { m_tc.movePosition(EndOfDocument, MoveAnchor); } void moveToStartOfLine() { m_tc.movePosition(StartOfLine, MoveAnchor); } void moveToEndOfLine(); @@ -1701,6 +1700,7 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0) static QRegExp reNormal("^norm(al)?( (.*))?$"); static QRegExp reSet("^set?( (.*))?$"); static QRegExp reWrite("^w!?( (.*))?$"); + static QRegExp reSubstitute("^s(.)(.*)\\1(.*)\\1([gi]*)"); if (cmd.isEmpty()) { setPosition(firstPositionInLine(beginLine)); @@ -1805,6 +1805,47 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0) enterCommandMode(); //qDebug() << "REPLAY: " << reNormal.cap(3); replay(reNormal.cap(3), 1); + } else if (reSubstitute.indexIn(cmd) != -1) { // :substitute + QString needle = reSubstitute.cap(2); + const QString replacement = reSubstitute.cap(3); + QString flags = reSubstitute.cap(4); + const bool startOfLineOnly = needle.startsWith('^'); + if (startOfLineOnly) + needle.remove(0, 1); + needle.replace('$', '\n'); + needle.replace("\\\n", "\\$"); + QRegExp pattern(needle); + if (flags.contains('i')) + pattern.setCaseSensitivity(Qt::CaseInsensitive); + const bool global = flags.contains('g'); + m_tc.beginEditBlock(); + for (int line = beginLine; line <= endLine; ++line) { + const int start = firstPositionInLine(line); + const int end = lastPositionInLine(line); + for (int position = start; position <= end && position >= start; ) { + position = pattern.indexIn(m_tc.document()->toPlainText(), position); + if (startOfLineOnly && position != start) + break; + if (position != -1) { + m_tc.setPosition(position); + m_tc.movePosition(QTextCursor::NextCharacter, + KeepAnchor, pattern.matchedLength()); + QString text = m_tc.selectedText(); + if (text.endsWith(ParagraphSeparator)) { + text = replacement + "\n"; + } else { + text.replace(ParagraphSeparator, "\n"); + text.replace(pattern, replacement); + } + m_tc.removeSelectedText(); + m_tc.insertText(text); + } + if (!global) + break; + } + } + m_tc.endEditBlock(); + enterCommandMode(); } else if (reSet.indexIn(cmd) != -1) { // :set showBlackMessage(QString()); QString arg = reSet.cap(2); @@ -1927,7 +1968,7 @@ void FakeVimHandler::Private::highlightMatches(const QString &needle0) if (!needle0.isEmpty()) { QTextCursor tc = m_tc; - tc.movePosition(QTextCursor::Start, MoveAnchor); + tc.movePosition(StartOfDocument, MoveAnchor); QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively; QString needle = needle0; @@ -2330,11 +2371,13 @@ void FakeVimHandler::Private::recordJump() UNDO_DEBUG("jumps: " << m_jumpListUndo); } -struct UndoBreaker : public QAbstractUndoItem +class UndoBreaker : public QAbstractUndoItem { +public: UndoBreaker(FakeVimHandler::Private *doc) : m_doc(doc) {} void undo() { m_doc->m_needMoreUndo = true; } void redo() { m_doc->m_needMoreUndo = true; } +private: FakeVimHandler::Private *m_doc; };