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/cdbassembler.cpp b/src/plugins/debugger/cdb/cdbassembler.cpp index 3d01d6d54ee9ea6613f82513e3169163c83baf54..00c34415ea2488d3056d66f5fe5317c24c0349a7 100644 --- a/src/plugins/debugger/cdb/cdbassembler.cpp +++ b/src/plugins/debugger/cdb/cdbassembler.cpp @@ -28,15 +28,20 @@ **************************************************************************/ #include "cdbassembler.h" -#include "registerhandler.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, @@ -79,5 +84,151 @@ bool getRegisters(IDebugControl4 *ctl, 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; +} + } } diff --git a/src/plugins/debugger/cdb/cdbassembler.h b/src/plugins/debugger/cdb/cdbassembler.h index e2386797e0499071b16834c4a174183388ce2719..d065707db14cd67e2cf17a1efb1a77e5945588a3 100644 --- a/src/plugins/debugger/cdb/cdbassembler.h +++ b/src/plugins/debugger/cdb/cdbassembler.h @@ -39,6 +39,8 @@ namespace Debugger { namespace Internal { +class DisassemblerLine; + // Utilities related to assembler code. class Register; @@ -47,6 +49,14 @@ bool getRegisters(IDebugControl4 *ctl, 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); } } diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index bdb692af941446490c8c787d06262796a269eb02..77efd2d4d0b264aac4c493874e44eaabc4958ebd 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -42,6 +42,7 @@ #include "watchhandler.h" #include "registerhandler.h" #include "moduleshandler.h" +#include "disassemblerhandler.h" #include "watchutils.h" #include <utils/qtcassert.h> @@ -56,6 +57,7 @@ #include <QtCore/QCoreApplication> #include <QtGui/QMessageBox> #include <QtGui/QMainWindow> +#include <QtGui/QApplication> #define DBGHELP_TRANSLATE_TCHAR #include <inc/Dbghelp.h> @@ -195,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), @@ -225,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)); @@ -317,6 +318,10 @@ CdbDebugEngine::CdbDebugEngine(DebuggerManager *parent) : 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() @@ -1040,7 +1045,31 @@ 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() diff --git a/src/plugins/debugger/cdb/cdbdebugoutput.cpp b/src/plugins/debugger/cdb/cdbdebugoutput.cpp index f0df999d28b13e739645a272b93fcf9174fdf30d..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,27 +60,43 @@ 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 CdbDebugOutputBase::Output( + THIS_ + IN ULONG mask, + IN PCWSTR 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_DEBUGGEE|DEBUG_OUTPUT_DEBUGGEE_PROMPT|DEBUG_OUTPUT_DEBUGGEE_PROMPT)) { - static const QString p = QLatin1String("target:"); - return p; - } if (mask & (DEBUG_OUTPUT_PROMPT_REGISTERS)) { static const QString p = QLatin1String("registers:"); return p; @@ -103,23 +117,42 @@ static QString prefix(ULONG mask) return commonPrefix; } -STDMETHODIMP CdbDebugOutput::Output( - THIS_ - IN ULONG mask, - IN PCSTR text - ) +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() { - const QString msg = QString::fromLocal8Bit(text); +} +void CdbDebugOutput::output(ULONG mask, const QString &msg) +{ if (debugCDB > 1) qDebug() << Q_FUNC_INFO << "\n " << msg; - if (mask & (DEBUG_OUTPUT_PROMPT|DEBUG_OUTPUT_DEBUGGEE_PROMPT)) { + switch (outputKind(mask)) { + case DebuggerOutput: + debuggerOutput(prefix(mask), msg); + break; + case DebuggerPromptOutput: emit debuggerInputPrompt(prefix(mask), msg); - } else { - emit debuggerOutput(prefix(mask), msg); + break; + case DebuggeeOutput: + emit debuggeeOutput(msg); + break; + case DebuggeePromptOutput: + emit debuggeeInputPrompt(msg); + break; } - return S_OK; } } // namespace Internal diff --git a/src/plugins/debugger/cdb/cdbdebugoutput.h b/src/plugins/debugger/cdb/cdbdebugoutput.h index 827d85bbb28a74aeb67e99b21ff3255c94cdd4ec..f13fedac35e57f1fc17444361daffeef0966ad11 100644 --- a/src/plugins/debugger/cdb/cdbdebugoutput.h +++ b/src/plugins/debugger/cdb/cdbdebugoutput.h @@ -38,14 +38,12 @@ 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 QObject, public IDebugOutputCallbacks +class CdbDebugOutputBase : public IDebugOutputCallbacksWide { - Q_OBJECT public: - explicit CdbDebugOutput(CdbDebugEngine* engine); - // IUnknown. STDMETHOD(QueryInterface)( THIS_ @@ -63,15 +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/cdbstacktracecontext.cpp b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp index 0500657dc44b12f7d05cb996a97a8cb46b4113fe..7d207fbba6640e629d645fcfd5db407be5950bdc 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); diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.h b/src/plugins/debugger/cdb/cdbstacktracecontext.h index bb8286859ef2ed3ade602808f5729a1be37a3511..4f3be5c5fc1d4559c0b5094e81b2c8ab0be9e9e8 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,6 +81,7 @@ private: DEBUG_STACK_FRAME m_cdbFrames[maxFrames]; QVector <CdbSymbolGroupContext*> m_symbolContexts; QList<StackFrame> m_frames; + ULONG64 m_instructionOffset; }; } diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp index cf2066c7909ac6d7a320e771dbc6a4e976218b18..9597c128c9b80a53bbdecf487ec20c277cb919f3 100644 --- a/src/plugins/debugger/debuggermanager.cpp +++ b/src/plugins/debugger/debuggermanager.cpp @@ -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; 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;