From e82d6c7b057b012b417c3493e0493dbb1b59e0c2 Mon Sep 17 00:00:00 2001 From: hjk <qtc-committer@nokia.com> Date: Fri, 14 Aug 2009 13:04:05 +0200 Subject: [PATCH] debugger: create a disassembler view as main editor "Stepping instruction wise" and "display disassembler" mode is now toggled by a single action. This also allows to re-use F10 and F11 as keyboard shortcut. Missing: caching of disassembler output, removal of old disassembler view. --- src/plugins/debugger/breakhandler.cpp | 20 +- src/plugins/debugger/breakhandler.h | 10 +- src/plugins/debugger/cdb/cdbdebugengine.cpp | 2 +- src/plugins/debugger/cpptools.cpp | 47 +++++ src/plugins/debugger/cpptools.h | 44 +++++ src/plugins/debugger/debuggeractions.cpp | 7 + src/plugins/debugger/debuggeractions.h | 1 + src/plugins/debugger/debuggeragents.cpp | 158 ++++++++++++++- src/plugins/debugger/debuggeragents.h | 35 +++- src/plugins/debugger/debuggerconstants.h | 2 - src/plugins/debugger/debuggermanager.cpp | 98 +++++----- src/plugins/debugger/debuggermanager.h | 17 +- src/plugins/debugger/debuggerplugin.cpp | 70 +++---- src/plugins/debugger/debuggerplugin.h | 10 +- src/plugins/debugger/debuggertooltip.h | 2 +- src/plugins/debugger/gdb/gdbengine.cpp | 195 ++++++++++++++++--- src/plugins/debugger/gdb/gdbengine.h | 11 ++ src/plugins/debugger/idebuggerengine.h | 8 +- src/plugins/debugger/registerhandler.cpp | 6 + src/plugins/debugger/registerwindow.cpp | 19 +- src/plugins/debugger/registerwindow.h | 6 +- src/plugins/debugger/script/scriptengine.cpp | 5 +- src/plugins/debugger/stackframe.h | 60 ++++++ src/plugins/debugger/stackhandler.cpp | 30 ++- src/plugins/debugger/stackhandler.h | 18 +- src/plugins/debugger/stackwindow.cpp | 67 +++++-- src/plugins/debugger/stackwindow.h | 7 +- src/plugins/debugger/watchwindow.cpp | 11 +- 28 files changed, 757 insertions(+), 209 deletions(-) create mode 100644 src/plugins/debugger/cpptools.cpp create mode 100644 src/plugins/debugger/cpptools.h create mode 100644 src/plugins/debugger/stackframe.h diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp index 7f333077c9d..ee6f75196d1 100644 --- a/src/plugins/debugger/breakhandler.cpp +++ b/src/plugins/debugger/breakhandler.cpp @@ -29,6 +29,9 @@ #include "breakhandler.h" +#include "debuggermanager.h" +#include "stackframe.h" + #include "imports.h" // TextEditor::BaseTextMark #include <QtCore/QDebug> @@ -242,8 +245,8 @@ bool BreakpointData::conditionsMatch() const // ////////////////////////////////////////////////////////////////// -BreakHandler::BreakHandler(QObject *parent) - : QAbstractItemModel(parent) +BreakHandler::BreakHandler(DebuggerManager *manager, QObject *parent) + : QAbstractItemModel(parent), m_manager(manager) { } @@ -345,13 +348,12 @@ void BreakHandler::saveBreakpoints() map.insert(QLatin1String("usefullpath"), QLatin1String("1")); list.append(map); } - setSessionValueRequested("Breakpoints", list); + m_manager->setSessionValue("Breakpoints", list); } void BreakHandler::loadBreakpoints() { - QVariant value; - sessionValueRequested("Breakpoints", &value); + QVariant value = m_manager->sessionValue("Breakpoints"); QList<QVariant> list = value.toList(); clear(); foreach (const QVariant &var, list) { @@ -677,8 +679,12 @@ void BreakHandler::activateBreakpoint(int index) { const BreakpointData *data = at(index); //qDebug() << "BREAKPOINT ACTIVATED: " << data->fileName; - if (!data->markerFileName.isEmpty()) - emit gotoLocation(data->markerFileName, data->markerLineNumber, false); + if (!data->markerFileName.isEmpty()) { + StackFrame frame; + frame.file = data->markerFileName; + frame.line = data->markerLineNumber; + m_manager->gotoLocation(frame, false); + } } void BreakHandler::breakByFunction(const QString &functionName) diff --git a/src/plugins/debugger/breakhandler.h b/src/plugins/debugger/breakhandler.h index 77c96f20eee..d02800625ca 100644 --- a/src/plugins/debugger/breakhandler.h +++ b/src/plugins/debugger/breakhandler.h @@ -40,6 +40,7 @@ namespace Internal { class BreakpointMarker; class BreakHandler; +class DebuggerManager; ////////////////////////////////////////////////////////////////// // @@ -113,7 +114,7 @@ class BreakHandler : public QAbstractItemModel Q_OBJECT public: - explicit BreakHandler(QObject *parent = 0); + explicit BreakHandler(DebuggerManager *manager, QObject *parent = 0); ~BreakHandler(); void removeAllBreakpoints(); @@ -149,12 +150,6 @@ public slots: void activateBreakpoint(int index); void removeBreakpoint(int index); -signals: - void gotoLocation(const QString &fileName, int lineNumber, bool setMarker); - - void sessionValueRequested(const QString &name, QVariant *value); - void setSessionValueRequested(const QString &name, const QVariant &value); - private: friend class BreakpointMarker; @@ -175,6 +170,7 @@ private: void resetBreakpoints(); void removeBreakpointHelper(int index); + DebuggerManager *m_manager; // not owned QList<BreakpointData *> m_bp; QList<BreakpointData *> m_inserted; // lately inserted breakpoints QList<BreakpointData *> m_removed; // lately removed breakpoints diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index c0732065937..51af8db4b70 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -1178,7 +1178,7 @@ void CdbDebugEngine::activateFrame(int frameIndex) break; } - m_d->m_debuggerManager->gotoLocation(frame.file, frame.line, true); + m_d->m_debuggerManager->gotoLocation(frame, true); if (oldIndex != frameIndex || m_d->m_firstActivatedFrame) { watchHandler->beginCycle(); diff --git a/src/plugins/debugger/cpptools.cpp b/src/plugins/debugger/cpptools.cpp new file mode 100644 index 00000000000..9b38e4615eb --- /dev/null +++ b/src/plugins/debugger/cpptools.cpp @@ -0,0 +1,47 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "cpptools.h" + +namespace Debugger { +namespace Internal { + +QByteArray mangleName(const QByteArray &ba) +{ + return ba; +} + + +QByteArray demangleName(const QByteArray &ba) +{ + return ba; +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/cpptools.h b/src/plugins/debugger/cpptools.h new file mode 100644 index 00000000000..e056d07cd45 --- /dev/null +++ b/src/plugins/debugger/cpptools.h @@ -0,0 +1,44 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DEBUGGER_CPPTOOLS_H +#define DEBUGGER_CPPTOOLS_H + +#include <QtCore/QByteArray> + +namespace Debugger { +namespace Internal { + +QByteArray mangleName(const QByteArray &ba); +QByteArray demangleName(const QByteArray &ba); + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_WATCHWINDOW_H diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp index 6c8f60bcb15..6b86696c0a5 100644 --- a/src/plugins/debugger/debuggeractions.cpp +++ b/src/plugins/debugger/debuggeractions.cpp @@ -151,6 +151,13 @@ DebuggerSettings *DebuggerSettings::instance() item->setDefaultValue(false); instance->insertItem(LogTimeStamps, item); + item = new SavedAction(instance); + item->setText(tr("Step by instruction")); + item->setCheckable(true); + item->setDefaultValue(false); + item->setIcon(QIcon(":/debugger/images/debugger_stepoverproc_small.png")); + instance->insertItem(StepByInstruction, item); + // // Locals & Watchers // diff --git a/src/plugins/debugger/debuggeractions.h b/src/plugins/debugger/debuggeractions.h index a929d2e8ccf..6d9656ffce0 100644 --- a/src/plugins/debugger/debuggeractions.h +++ b/src/plugins/debugger/debuggeractions.h @@ -77,6 +77,7 @@ enum DebuggerActionCode AutoQuit, LockView, LogTimeStamps, + StepByInstruction, RecheckDebuggingHelpers, UseDebuggingHelpers, diff --git a/src/plugins/debugger/debuggeragents.cpp b/src/plugins/debugger/debuggeragents.cpp index 3f6550f1d76..8e5e5bc1125 100644 --- a/src/plugins/debugger/debuggeragents.cpp +++ b/src/plugins/debugger/debuggeragents.cpp @@ -30,15 +30,59 @@ #include "debuggeragents.h" #include "idebuggerengine.h" +#include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/icore.h> + +#include <texteditor/basetexteditor.h> +#include <texteditor/basetextmark.h> +#include <texteditor/itexteditor.h> +#include <texteditor/texteditorconstants.h> + +#include <utils/qtcassert.h> + +#include <QtGui/QPlainTextEdit> +#include <QtGui/QTextCursor> #include <limits.h> namespace Debugger { namespace Internal { +/////////////////////////////////////////////////////////////////////// +// +// MemoryViewAgent +// +/////////////////////////////////////////////////////////////////////// + +/*! + \class MemoryViewAgent + + Objects form this class are created in response to user actions in + the Gui for showing raw memory from the inferior. After creation + it handles communication between the engine and the bineditor. +*/ + MemoryViewAgent::MemoryViewAgent(DebuggerManager *manager, quint64 addr) : QObject(manager), m_engine(manager->currentEngine()) +{ + init(addr); +} + +MemoryViewAgent::MemoryViewAgent(DebuggerManager *manager, const QString &addr) + : QObject(manager), m_engine(manager->currentEngine()) +{ + bool ok = true; + init(addr.toUInt(&ok, 0)); +} + +MemoryViewAgent::~MemoryViewAgent() +{ + m_editor->deleteLater(); +} + +void MemoryViewAgent::init(quint64 addr) { Core::EditorManager *editorManager = Core::EditorManager::instance(); QString titlePattern = "Memory $"; @@ -49,12 +93,7 @@ MemoryViewAgent::MemoryViewAgent(DebuggerManager *manager, quint64 addr) this, SLOT(fetchLazyData(int,bool))); editorManager->activateEditor(m_editor); QMetaObject::invokeMethod(m_editor->widget(), "setLazyData", - Q_ARG(int, addr), Q_ARG(int, INT_MAX), Q_ARG(int, BinBlockSize)); -} - -MemoryViewAgent::~MemoryViewAgent() -{ - m_editor->deleteLater(); + Q_ARG(int, addr), Q_ARG(int, INT_MAX), Q_ARG(int, BinBlockSize)); } void MemoryViewAgent::fetchLazyData(int block, bool sync) @@ -70,6 +109,113 @@ void MemoryViewAgent::addLazyData(quint64 addr, const QByteArray &ba) } +/////////////////////////////////////////////////////////////////////// +// +// DisassemblerViewAgent +// +/////////////////////////////////////////////////////////////////////// + +static QIcon locationMarkIcon() +{ + static const QIcon icon(":/debugger/images/location.svg"); + return icon; +} + +// Used for the disassembler view +class LocationMark2 : public TextEditor::ITextMark +{ +public: + LocationMark2() {} + + QIcon icon() const { return locationMarkIcon(); } + void updateLineNumber(int /*lineNumber*/) {} + void updateBlock(const QTextBlock & /*block*/) {} + void removedFromEditor() {} + void documentClosing() {} +}; + +struct DisassemblerViewAgentPrivate +{ + QPointer<TextEditor::ITextEditor> editor; + QPointer<IDebuggerEngine> engine; + QString address; + LocationMark2 *locationMark; +}; + +/*! + \class DisassemblerViewAgent + + Objects from this class are created in response to user actions in + the Gui for showing disassembled memory from the inferior. After creation + it handles communication between the engine and the editor. +*/ + +DisassemblerViewAgent::DisassemblerViewAgent(DebuggerManager *manager) + : QObject(manager), d(new DisassemblerViewAgentPrivate) +{ + d->editor = 0; + d->engine = manager->currentEngine(); + d->locationMark = new LocationMark2(); +} + +DisassemblerViewAgent::~DisassemblerViewAgent() +{ + if (d->editor) + d->editor->deleteLater(); + delete d; +} + +void DisassemblerViewAgent::setFrame(const StackFrame &frame) +{ + d->engine->fetchDisassembler(this, frame); + d->address = frame.address; +} + +void DisassemblerViewAgent::setContents(const QString &contents) +{ + using namespace Core; + using namespace TextEditor; + + EditorManager *editorManager = EditorManager::instance(); + if (!d->editor) { + QString titlePattern = "Disassembler"; + d->editor = qobject_cast<ITextEditor *>( + editorManager->openEditorWithContents( + Core::Constants::K_DEFAULT_TEXT_EDITOR, + &titlePattern)); + } + + editorManager->activateEditor(d->editor); + + QPlainTextEdit *plainTextEdit = + qobject_cast<QPlainTextEdit *>(d->editor->widget()); + if (plainTextEdit) + plainTextEdit->setPlainText(contents); + + d->editor->markableInterface()->removeMark(d->locationMark); + + for (int pos = 0, line = 0; ; ++line, ++pos) { + if (contents.midRef(pos, d->address.size()) == d->address) { + d->editor->markableInterface()->addMark(d->locationMark, line + 1); + if (plainTextEdit) { + QTextCursor tc = plainTextEdit->textCursor(); + tc.setPosition(pos); + plainTextEdit->setTextCursor(tc); + } + break; + } + pos = contents.indexOf('\n', pos + 1); + if (pos == -1) + break; + } +} + +QString DisassemblerViewAgent::address() const +{ + return d->address; +} + } // namespace Internal } // namespace Debugger +#include "debuggeragents.moc" diff --git a/src/plugins/debugger/debuggeragents.h b/src/plugins/debugger/debuggeragents.h index f9984b84933..b48eb703af6 100644 --- a/src/plugins/debugger/debuggeragents.h +++ b/src/plugins/debugger/debuggeragents.h @@ -31,26 +31,21 @@ #define DEBUGGER_AGENTS_H #include "debuggermanager.h" +#include "stackframe.h" -#include <coreplugin/icore.h> -#include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/ieditor.h> -#include <utils/qtcassert.h> - #include <QtCore/QObject> #include <QtCore/QDebug> #include <QtCore/QPointer> #include <QtGui/QAction> + namespace Debugger { namespace Internal { class DebuggerManager; - -// Object form this class are created in response to user actions in -// the Gui for showing raw memory from the inferior. After creation -// it handles communication between the engine and the bineditor. +class DisassemblerViewAgentPrivate; class MemoryViewAgent : public QObject { @@ -59,6 +54,7 @@ class MemoryViewAgent : public QObject public: // Called from Gui MemoryViewAgent(DebuggerManager *manager, quint64 startaddr); + MemoryViewAgent(DebuggerManager *manager, const QString &startaddr); ~MemoryViewAgent(); enum { BinBlockSize = 1024 }; @@ -69,11 +65,32 @@ public slots: // Called from Editor void fetchLazyData(int block, bool sync); -public: +private: + void init(quint64 startaddr); + QPointer<IDebuggerEngine> m_engine; QPointer<Core::IEditor> m_editor; }; + +class DisassemblerViewAgent : public QObject +{ + Q_OBJECT + +public: + // Called from Gui + DisassemblerViewAgent(DebuggerManager *manager); + ~DisassemblerViewAgent(); + + void setFrame(const StackFrame &frame); + Q_SLOT void setContents(const QString &contents); + QString address() const; + +private: + DisassemblerViewAgentPrivate *d; +}; + + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h index 0ff77414952..337ab8ba342 100644 --- a/src/plugins/debugger/debuggerconstants.h +++ b/src/plugins/debugger/debuggerconstants.h @@ -45,8 +45,6 @@ const char * const RESET = "Debugger.Reset"; const char * const STEP = "Debugger.StepLine"; const char * const STEPOUT = "Debugger.StepOut"; const char * const NEXT = "Debugger.NextLine"; -const char * const STEPI = "Debugger.StepInstruction"; -const char * const NEXTI = "Debugger.NextInstruction"; const char * const REVERSE = "Debugger.ReverseDirection"; const char * const M_DEBUG_VIEWS = "Debugger.Menu.View.Debug"; diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp index 6d81c5ecdd0..76b253726be 100644 --- a/src/plugins/debugger/debuggermanager.cpp +++ b/src/plugins/debugger/debuggermanager.cpp @@ -219,8 +219,8 @@ void DebuggerManager::init() m_disassemblerWindow = new DisassemblerWindow; m_modulesWindow = new ModulesWindow(this); m_outputWindow = new DebuggerOutputWindow; - m_registerWindow = new RegisterWindow; - m_stackWindow = new StackWindow; + m_registerWindow = new RegisterWindow(this); + m_stackWindow = new StackWindow(this); m_sourceFilesWindow = new SourceFilesWindow; m_threadsWindow = new ThreadsWindow; m_localsWindow = new WatchWindow(WatchWindow::LocalsType, this); @@ -261,7 +261,7 @@ void DebuggerManager::init() this, SLOT(reloadDisassembler())); // Breakpoints - m_breakHandler = new BreakHandler; + m_breakHandler = new BreakHandler(this); QAbstractItemView *breakView = qobject_cast<QAbstractItemView *>(m_breakWindow); breakView->setModel(m_breakHandler->model()); @@ -271,12 +271,6 @@ void DebuggerManager::init() m_breakHandler, SLOT(removeBreakpoint(int))); connect(breakView, SIGNAL(breakpointSynchronizationRequested()), this, SLOT(attemptBreakpointSynchronization())); - connect(m_breakHandler, SIGNAL(gotoLocation(QString,int,bool)), - this, SLOT(gotoLocation(QString,int,bool))); - connect(m_breakHandler, SIGNAL(sessionValueRequested(QString,QVariant*)), - this, SIGNAL(sessionValueRequested(QString,QVariant*))); - connect(m_breakHandler, SIGNAL(setSessionValueRequested(QString,QVariant)), - this, SIGNAL(setSessionValueRequested(QString,QVariant))); connect(breakView, SIGNAL(breakByFunctionRequested(QString)), this, SLOT(breakByFunction(QString)), Qt::QueuedConnection); connect(breakView, SIGNAL(breakByFunctionMainRequested()), @@ -357,16 +351,6 @@ void DebuggerManager::init() //m_stepAction->setShortcut(QKeySequence(tr("F7"))); m_stepAction->setIcon(QIcon(":/debugger/images/debugger_stepinto_small.png")); - m_nextIAction = new QAction(this); - m_nextIAction->setText(tr("Step Over Instruction")); - //m_nextIAction->setShortcut(QKeySequence(tr("Shift+F6"))); - m_nextIAction->setIcon(QIcon(":/debugger/images/debugger_stepoverproc_small.png")); - - m_stepIAction = new QAction(this); - m_stepIAction->setText(tr("Step One Instruction")); - //m_stepIAction->setShortcut(QKeySequence(tr("Shift+F9"))); - m_stepIAction->setIcon(QIcon(":/debugger/images/debugger_steponeproc_small.png")); - m_stepOutAction = new QAction(this); m_stepOutAction->setText(tr("Step Out")); //m_stepOutAction->setShortcut(QKeySequence(tr("Shift+F7"))); @@ -405,10 +389,8 @@ void DebuggerManager::init() this, SLOT(nextExec())); connect(m_stepAction, SIGNAL(triggered()), this, SLOT(stepExec())); - connect(m_nextIAction, SIGNAL(triggered()), - this, SLOT(nextIExec())); - connect(m_stepIAction, SIGNAL(triggered()), - this, SLOT(stepIExec())); + connect(theDebuggerAction(StepByInstruction), SIGNAL(triggered()), + this, SLOT(stepByInstructionTriggered())); connect(m_stepOutAction, SIGNAL(triggered()), this, SLOT(stepOutExec())); connect(m_runToLineAction, SIGNAL(triggered()), @@ -427,9 +409,14 @@ void DebuggerManager::init() connect(theDebuggerAction(ExecuteCommand), SIGNAL(triggered()), this, SLOT(executeDebuggerCommand())); + connect(theDebuggerAction(WatchPoint), SIGNAL(triggered()), this, SLOT(watchPoint())); + connect(theDebuggerAction(StepByInstruction), SIGNAL(triggered()), + this, SLOT(stepByInstructionTriggered())); + + m_breakDock = m_mainWindow->addDockForWidget(m_breakWindow); m_disassemblerDock = m_mainWindow->addDockForWidget(m_disassemblerWindow); @@ -725,14 +712,6 @@ void DebuggerManager::updateWatchData(const WatchData &data) m_engine->updateWatchData(data); } -QVariant DebuggerManager::sessionValue(const QString &name) -{ - // this is answered by the plugin - QVariant value; - emit sessionValueRequested(name, &value); - return value; -} - static inline QString msgEngineNotAvailable(const char *engine) { return DebuggerManager::tr("The application requires the debugger engine '%1', which is disabled.").arg(QLatin1String(engine)); @@ -986,7 +965,10 @@ void DebuggerManager::stepExec() { QTC_ASSERT(m_engine, return); resetLocation(); - m_engine->stepExec(); + if (theDebuggerBoolSetting(StepByInstruction)) + m_engine->stepIExec(); + else + m_engine->stepExec(); } void DebuggerManager::stepOutExec() @@ -1000,21 +982,10 @@ void DebuggerManager::nextExec() { QTC_ASSERT(m_engine, return); resetLocation(); - m_engine->nextExec(); -} - -void DebuggerManager::stepIExec() -{ - QTC_ASSERT(m_engine, return); - resetLocation(); - m_engine->stepIExec(); -} - -void DebuggerManager::nextIExec() -{ - QTC_ASSERT(m_engine, return); - resetLocation(); - m_engine->nextIExec(); + if (theDebuggerBoolSetting(StepByInstruction)) + m_engine->nextIExec(); + else + m_engine->nextExec(); } void DebuggerManager::watchPoint() @@ -1190,8 +1161,6 @@ void DebuggerManager::setStatus(int status) m_runToFunctionAction->setEnabled(ready); m_jumpToLineAction->setEnabled(ready); m_nextAction->setEnabled(ready); - m_stepIAction->setEnabled(ready); - m_nextIAction->setEnabled(ready); //showStatusMessage(QString("started: %1, running: %2").arg(started).arg(running)); emit statusChanged(m_status); const bool notbusy = ready || status == DebuggerProcessNotReady; @@ -1316,17 +1285,26 @@ void DebuggerManager::resetLocation() emit resetLocationRequested(); } -void DebuggerManager::gotoLocation(const QString &fileName, int line, - bool setMarker) +void DebuggerManager::gotoLocation(const StackFrame &frame, bool setMarker) { // connected to the plugin - emit gotoLocationRequested(fileName, line, setMarker); + emit gotoLocationRequested(frame, setMarker); } void DebuggerManager::fileOpen(const QString &fileName) { // connected to the plugin - emit gotoLocationRequested(fileName, 1, false); + StackFrame frame; + frame.file = fileName; + frame.line = -1; + emit gotoLocationRequested(frame, false); +} + +void DebuggerManager::stepByInstructionTriggered() +{ + QTC_ASSERT(m_stackHandler, return); + StackFrame frame = m_stackHandler->currentFrame(); + gotoLocation(frame, true); } @@ -1497,6 +1475,20 @@ bool DebuggerManager::isReverseDebugging() const return m_reverseDirectionAction->isChecked(); } +QVariant DebuggerManager::sessionValue(const QString &name) +{ + // this is answered by the plugin + QVariant value; + emit sessionValueRequested(name, &value); + return value; +} + +void DebuggerManager::setSessionValue(const QString &name, const QVariant &value) +{ + emit setSessionValueRequested(name, value); +} + + ////////////////////////////////////////////////////////////////////// // // Testing diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h index 65aa96f8099..8bf7e2a3aef 100644 --- a/src/plugins/debugger/debuggermanager.h +++ b/src/plugins/debugger/debuggermanager.h @@ -77,6 +77,7 @@ class DisassemblerHandler; class ModulesHandler; class RegisterHandler; class SourceFilesWindow; +class StackFrame; class StackHandler; class Symbol; class ThreadsHandler; @@ -141,8 +142,9 @@ enum LogChannel LogMisc }; -struct DebuggerStartParameters +class DebuggerStartParameters { +public: DebuggerStartParameters(); void clear(); @@ -281,9 +283,8 @@ public slots: void setBusyCursor(bool on); void queryCurrentTextEditor(QString *fileName, int *lineNumber, QObject **ed); - QVariant sessionValue(const QString &name); - void gotoLocation(const QString &file, int line, bool setLocationMarker); + void gotoLocation(const StackFrame &frame, bool setLocationMarker); void fileOpen(const QString &file); void resetLocation(); @@ -302,8 +303,6 @@ public slots: void stepExec(); void stepOutExec(); void nextExec(); - void stepIExec(); - void nextIExec(); void continueExec(); void detachDebugger(); @@ -313,6 +312,8 @@ public slots: void sessionLoaded(); void aboutToUnloadSession(); void aboutToSaveSession(); + QVariant sessionValue(const QString &name); + void setSessionValue(const QString &name, const QVariant &value); void assignValueInDebugger(); void assignValueInDebugger(const QString &expr, const QString &value); @@ -346,6 +347,7 @@ private slots: void clearStatusMessage(); void attemptBreakpointSynchronization(); void reloadFullStack(); + void stepByInstructionTriggered(); private: // @@ -404,7 +406,7 @@ signals: void debugModeRequested(); void previousModeRequested(); void statusMessageRequested(const QString &msg, int timeout); // -1 for 'forever' - void gotoLocationRequested(const QString &file, int line, bool setLocationMarker); + void gotoLocationRequested(const StackFrame &frame, bool setLocationMarker); void resetLocationRequested(); void currentTextEditorRequested(QString *fileName, int *lineNumber, QObject **ob); void sessionValueRequested(const QString &name, QVariant *value); @@ -468,8 +470,7 @@ private: QAction *m_watchAction; QAction *m_breakAction; QAction *m_sepAction; - QAction *m_stepIAction; - QAction *m_nextIAction; + //QActio *m_stepByInstructionAction; QAction *m_reverseDirectionAction; QWidget *m_breakWindow; diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 93f660b21b5..475f566aff5 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -31,10 +31,12 @@ #include "breakhandler.h" #include "debuggeractions.h" +#include "debuggeragents.h" #include "debuggerdialogs.h" #include "debuggerconstants.h" #include "debuggermanager.h" #include "debuggerrunner.h" +#include "stackframe.h" #include "ui_commonoptionspage.h" #include "ui_dumperoptionpage.h" @@ -116,6 +118,7 @@ const char * const TOGGLE_BREAK = "Debugger.ToggleBreak"; const char * const BREAK_BY_FUNCTION = "Debugger.BreakByFunction"; const char * const BREAK_AT_MAIN = "Debugger.BreakAtMain"; const char * const ADD_TO_WATCH = "Debugger.AddToWatch"; +const char * const STEP_BY_INSTRUCTION = "Debugger.StepByInstruction"; #ifdef Q_WS_MAC const char * const INTERRUPT_KEY = "Shift+F5"; @@ -123,8 +126,6 @@ const char * const RESET_KEY = "Ctrl+Shift+F5"; const char * const STEP_KEY = "F7"; const char * const STEPOUT_KEY = "Shift+F7"; const char * const NEXT_KEY = "F6"; -const char * const STEPI_KEY = "Shift+F9"; -const char * const NEXTI_KEY = "Shift+F6"; const char * const REVERSE_KEY = ""; const char * const RUN_TO_LINE_KEY = "Shift+F8"; const char * const RUN_TO_FUNCTION_KEY = "Ctrl+F6"; @@ -139,8 +140,6 @@ const char * const RESET_KEY = "Ctrl+Shift+F5"; const char * const STEP_KEY = "F11"; const char * const STEPOUT_KEY = "Shift+F11"; const char * const NEXT_KEY = "F10"; -const char * const STEPI_KEY = ""; -const char * const NEXTI_KEY = ""; const char * const REVERSE_KEY = "F12"; const char * const RUN_TO_LINE_KEY = ""; const char * const RUN_TO_FUNCTION_KEY = ""; @@ -218,6 +217,13 @@ DebugMode::~DebugMode() namespace Debugger { namespace Internal { +static QIcon locationMarkIcon() +{ + static const QIcon icon(":/debugger/images/location.svg"); + return icon; +} + +// Used in "real" editors class LocationMark : public TextEditor::BaseTextMark { Q_OBJECT @@ -226,25 +232,13 @@ public: LocationMark(const QString &fileName, int linenumber) : BaseTextMark(fileName, linenumber) {} - ~LocationMark(); - QIcon icon() const; + QIcon icon() const { return locationMarkIcon(); } void updateLineNumber(int /*lineNumber*/) {} void updateBlock(const QTextBlock & /*block*/) {} void removedFromEditor() {} }; -LocationMark::~LocationMark() -{ - //qDebug() << "LOCATIONMARK DESTRUCTOR"; -} - -QIcon LocationMark::icon() const -{ - static const QIcon icon(":/debugger/images/location.svg"); - return icon; -} - } // namespace Internal } // namespace Debugger @@ -422,6 +416,7 @@ DebuggerPlugin::DebuggerPlugin() : m_manager(0), m_debugMode(0), m_locationMark(0), + m_disassemblerViewAgent(0), m_gdbRunningContext(0), m_cmdLineEnabledEngines(AllEngineTypes), m_cmdLineAttachPid(0), @@ -682,14 +677,8 @@ bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMess cmd->setDefaultKeySequence(QKeySequence(Constants::STEPOUT_KEY)); mdebug->addAction(cmd); - cmd = am->registerAction(m_manager->m_nextIAction, - Constants::NEXTI, debuggercontext); - cmd->setDefaultKeySequence(QKeySequence(Constants::NEXTI_KEY)); - mdebug->addAction(cmd); - - cmd = am->registerAction(m_manager->m_stepIAction, - Constants::STEPI, debuggercontext); - cmd->setDefaultKeySequence(QKeySequence(Constants::STEPI_KEY)); + cmd = am->registerAction(theDebuggerAction(StepByInstruction), + Constants::STEP_BY_INSTRUCTION, debuggercontext); mdebug->addAction(cmd); cmd = am->registerAction(m_manager->m_runToLineAction, @@ -831,9 +820,7 @@ bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMess debugToolBarLayout->addWidget(toolButton(am->command(Constants::NEXT)->action())); debugToolBarLayout->addWidget(toolButton(am->command(Constants::STEP)->action())); debugToolBarLayout->addWidget(toolButton(am->command(Constants::STEPOUT)->action())); - debugToolBarLayout->addWidget(new Core::Utils::StyledSeparator); - debugToolBarLayout->addWidget(toolButton(am->command(Constants::STEPI)->action())); - debugToolBarLayout->addWidget(toolButton(am->command(Constants::NEXTI)->action())); + debugToolBarLayout->addWidget(toolButton(am->command(Constants::STEP_BY_INSTRUCTION)->action())); #ifdef USE_REVERSE_DEBUGGING debugToolBarLayout->addWidget(new Core::Utils::StyledSeparator); debugToolBarLayout->addWidget(toolButton(am->command(Constants::REVERSE)->action())); @@ -892,8 +879,8 @@ bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMess connect(m_manager, SIGNAL(resetLocationRequested()), this, SLOT(resetLocation())); - connect(m_manager, SIGNAL(gotoLocationRequested(QString,int,bool)), - this, SLOT(gotoLocation(QString,int,bool))); + connect(m_manager, SIGNAL(gotoLocationRequested(StackFrame,bool)), + this, SLOT(gotoLocation(StackFrame,bool))); connect(m_manager, SIGNAL(statusChanged(int)), this, SLOT(changeStatus(int))); connect(m_manager, SIGNAL(previousModeRequested()), @@ -1096,17 +1083,23 @@ void DebuggerPlugin::resetLocation() m_locationMark = 0; } -void DebuggerPlugin::gotoLocation(const QString &fileName, int lineNumber, - bool setMarker) +void DebuggerPlugin::gotoLocation(const StackFrame &frame, bool setMarker) { - TextEditor::BaseTextEditor::openEditorAt(fileName, lineNumber); - if (setMarker) { - resetLocation(); - m_locationMark = new LocationMark(fileName, lineNumber); + if (theDebuggerBoolSetting(StepByInstruction) || !frame.isUsable()) { + if (!m_disassemblerViewAgent) + m_disassemblerViewAgent = new DisassemblerViewAgent(m_manager); + m_disassemblerViewAgent->setFrame(frame); + if (setMarker) + resetLocation(); + } else { + TextEditor::BaseTextEditor::openEditorAt(frame.file, frame.line); + if (setMarker) { + resetLocation(); + m_locationMark = new LocationMark(frame.file, frame.line); + } } } - void DebuggerPlugin::changeStatus(int status) { bool startIsContinue = (status == DebuggerInferiorStopped); @@ -1157,9 +1150,8 @@ void DebuggerPlugin::onModeChanged(IMode *mode) // different then the debugger mode. E.g. Welcome and Help mode and // also on shutdown. - if (mode != m_debugMode) { + if (mode != m_debugMode) return; - } EditorManager *editorManager = EditorManager::instance(); if (editorManager->currentEditor()) diff --git a/src/plugins/debugger/debuggerplugin.h b/src/plugins/debugger/debuggerplugin.h index 01fe816d324..74928abe447 100644 --- a/src/plugins/debugger/debuggerplugin.h +++ b/src/plugins/debugger/debuggerplugin.h @@ -50,6 +50,8 @@ class IMode; namespace TextEditor { class ITextEditor; +class ITextMark; +class BaseTextMark; } namespace Debugger { @@ -59,7 +61,8 @@ class BreakpointData; class DebuggerManager; class DebuggerRunner; class DebugMode; -class LocationMark; +class DisassemblerViewAgent; +class StackFrame; class DebuggerPlugin : public ExtensionSystem::IPlugin { @@ -94,7 +97,7 @@ private slots: void updateActions(int status); void resetLocation(); - void gotoLocation(const QString &fileName, int line, bool setMarker); + void gotoLocation(const StackFrame &frame, bool setMarker); void breakpointSetRemoveMarginActionTriggered(); void breakpointEnableDisableMarginActionTriggered(); @@ -127,7 +130,8 @@ private: DebuggerRunner *m_debuggerRunner; QString m_previousMode; - LocationMark *m_locationMark; + TextEditor::BaseTextMark *m_locationMark; + DisassemblerViewAgent *m_disassemblerViewAgent; int m_gdbRunningContext; unsigned m_cmdLineEnabledEngines; quint64 m_cmdLineAttachPid; diff --git a/src/plugins/debugger/debuggertooltip.h b/src/plugins/debugger/debuggertooltip.h index 59ff8807745..c543923381f 100644 --- a/src/plugins/debugger/debuggertooltip.h +++ b/src/plugins/debugger/debuggertooltip.h @@ -30,7 +30,7 @@ #ifndef DEBUGGER_DEBUGGERTOOLTIP_H #define DEBUGGER_DEBUGGERTOOLTIP_H -#include <qglobal.h> +#include <QtCore/QtGlobal> QT_BEGIN_NAMESPACE class QAbstractItemModel; diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 567f4e24dc5..74e8f6d13c9 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -970,11 +970,11 @@ void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record, const QVa qq->notifyInferiorStopped(); q->showStatusMessage(tr("Run to Function finished. Stopped.")); GdbMi frame = record.data.findChild("frame"); - QString file = QString::fromLocal8Bit(frame.findChild("fullname").data()); - int line = frame.findChild("line").data().toInt(); - qDebug() << "HIT:" << file << line << "IN" << frame.toString() - << "--" << record.toString(); - q->gotoLocation(file, line, true); + StackFrame f; + f.file = QString::fromLocal8Bit(frame.findChild("fullname").data()); + f.line = frame.findChild("line").data().toInt(); + f.address = _(frame.findChild("addr").data()); + q->gotoLocation(f, true); } static bool isExitedReason(const QByteArray &reason) @@ -1229,11 +1229,11 @@ void GdbEngine::handleAsyncOutput(const GdbMi &data) qq->notifyInferiorStopped(); q->showStatusMessage(tr("Run to Function finished. Stopped.")); GdbMi frame = data.findChild("frame"); - QString file = QString::fromLocal8Bit(frame.findChild("fullname").data()); - int line = frame.findChild("line").data().toInt(); - qDebug() << "HIT:" << file << line << "IN" << frame.toString() - << "--" << data.toString(); - q->gotoLocation(file, line, true); + StackFrame f; + f.file = QString::fromLocal8Bit(frame.findChild("fullname").data()); + f.line = frame.findChild("line").data().toInt(); + f.address = _(frame.findChild("addr").data()); + q->gotoLocation(f, true); #endif } @@ -1853,6 +1853,9 @@ void GdbEngine::runToFunctionExec(const QString &functionName) void GdbEngine::jumpToLineExec(const QString &fileName, int lineNumber) { + StackFrame frame; + frame.file = fileName; + frame.line = lineNumber; #if 1 // not available everywhere? //sendCliCommand(_("tbreak ") + fileName + ':' + QString::number(lineNumber)); @@ -1864,11 +1867,11 @@ void GdbEngine::jumpToLineExec(const QString &fileName, int lineNumber) // ~"run1 (argc=1, argv=0x7fffbf1f5538) at test1.cpp:242" // ~"242\t x *= 2;" // 23^done" - q->gotoLocation(fileName, lineNumber, true); + q->gotoLocation(frame, true); //setBreakpoint(); //postCommand(_("jump ") + fileName + ':' + QString::number(lineNumber)); #else - q->gotoLocation(fileName, lineNumber, true); + q->gotoLocation(frame, true); setBreakpoint(fileName, lineNumber); postCommand(_("jump ") + fileName + ':' + QString::number(lineNumber)); #endif @@ -2566,13 +2569,9 @@ void GdbEngine::handleStackListFrames(const GdbResultRecord &record, const QVari theDebuggerAction(ExpandStack)->setEnabled(canExpand); qq->stackHandler()->setFrames(stackFrames, canExpand); - if (topFrame != -1) { - // updates of locals already triggered early + if (topFrame != -1 || theDebuggerBoolSetting(StepByInstruction)) { const StackFrame &frame = qq->stackHandler()->currentFrame(); - if (frame.isUsable()) - q->gotoLocation(frame.file, frame.line, true); - else - qDebug() << "FULL NAME NOT USABLE 0:" << frame.file << topFrame; + q->gotoLocation(frame, true); } } @@ -2622,7 +2621,7 @@ void GdbEngine::activateFrame(int frameIndex) const StackFrame &frame = stackHandler->currentFrame(); if (frame.isUsable()) - q->gotoLocation(frame.file, frame.line, true); + q->gotoLocation(frame, true); else qDebug() << "FULL NAME NOT USABLE:" << frame.file; } @@ -4004,6 +4003,18 @@ void GdbEngine::handleWatchPoint(const GdbResultRecord &record, const QVariant & } } +static QVariant agentCookie(void *agent) +{ + return QVariant(quint64(quintptr(agent))); +} + +void GdbEngine::fetchMemory(MemoryViewAgent *agent, quint64 addr, quint64 length) +{ + //qDebug() << "GDB MEMORY FETCH" << addr << length; + postCommand(_("-data-read-memory %1 x 1 1 %2").arg(addr).arg(length), + NeedsStop, CB(handleFetchMemory), agentCookie(agent)); +} + void GdbEngine::handleFetchMemory(const GdbResultRecord &record, const QVariant &cookie) { @@ -4017,7 +4028,7 @@ void GdbEngine::handleFetchMemory(const GdbResultRecord &record, QTC_ASSERT(agent, return); QByteArray ba; GdbMi memory = record.data.findChild("memory"); - QTC_ASSERT(memory.children().size() == 1, return); + QTC_ASSERT(memory.children().size() <= 1, return); GdbMi memory0 = memory.children().at(0); // we asked for only one 'row' quint64 addr = memory0.findChild("addr").data().toULongLong(&ok, 0); QTC_ASSERT(ok, return); @@ -4031,13 +4042,149 @@ void GdbEngine::handleFetchMemory(const GdbResultRecord &record, agent->addLazyData(addr, ba); } -void GdbEngine::fetchMemory(MemoryViewAgent *agent, quint64 addr, quint64 length) + +void GdbEngine::fetchDisassembler(DisassemblerViewAgent *agent, + const StackFrame &frame) { - //qDebug() << "GDB MEMORY FETCH" << addr << length; - postCommand(_("-data-read-memory %1 x 1 1 %2").arg(addr).arg(length), - NeedsStop, CB(handleFetchMemory), QVariant(quint64(agent))); + if (frame.file.isEmpty()) { + fetchDisassemblerByAddress(agent, true); + } else { + // Disassemble full function: + QString cmd = _("-data-disassemble -f %1 -l %2 -n -1 -- 1"); + postCommand(cmd.arg(frame.file).arg(frame.line), + Discardable, CB(handleFetchDisassemblerByLine), agentCookie(agent)); + } +} + +void GdbEngine::fetchDisassemblerByAddress(DisassemblerViewAgent *agent, + bool useMixedMode) +{ + QTC_ASSERT(agent, return); + bool ok = true; + quint64 address = agent->address().toULongLong(&ok, 0); + quint64 start = address - 20; + quint64 end = address + 100; + // -data-disassemble [ -s start-addr -e end-addr ] + // | [ -f filename -l linenum [ -n lines ] ] -- mode + if (useMixedMode) + postCommand(_("-data-disassemble -s %1 -e %2 -- 1").arg(start).arg(end), + Discardable, CB(handleFetchDisassemblerByAddress1), agentCookie(agent)); + else + postCommand(_("-data-disassemble -s %1 -e %2 -- 0").arg(start).arg(end), + Discardable, CB(handleFetchDisassemblerByAddress0), agentCookie(agent)); +} + +static QByteArray parseLine(const GdbMi &line) +{ + QByteArray ba; + ba.reserve(200); + QByteArray address = line.findChild("address").data(); + QByteArray funcName = line.findChild("func-name").data(); + QByteArray offset = line.findChild("offset").data(); + QByteArray inst = line.findChild("inst").data(); + ba += address + QByteArray(15 - address.size(), ' '); + ba += funcName + "+" + offset + " "; + ba += QByteArray(30 - funcName.size() - offset.size(), ' '); + ba += inst; + ba += '\n'; + return ba; +} + +static QString parseDisassembler(const GdbMi &lines) +{ + // ^done,data={asm_insns=[src_and_asm_line={line="1243",file=".../app.cpp", + // line_asm_insn=[{address="0x08054857",func-name="main",offset="27", + // inst="call 0x80545b0 <_Z13testQFileInfov>"}]}, + // src_and_asm_line={line="1244",file=".../app.cpp", + // line_asm_insn=[{address="0x0805485c",func-name="main",offset="32", + //inst="call 0x804cba1 <_Z11testObject1v>"}]}]} + // - or - + // ^done,asm_insns=[ + // {address="0x0805acf8",func-name="...",offset="25",inst="and $0xe8,%al"}, + // {address="0x0805acfa",func-name="...",offset="27",inst="pop %esp"}, + + QList<QByteArray> fileContents; + bool fileLoaded = false; + QByteArray ba; + ba.reserve(200 * lines.children().size()); + + // FIXME: Performance? + foreach (const GdbMi &child, lines.children()) { + if (child.hasName("src_and_asm_line")) { + // mixed mode + int line = child.findChild("line").data().toInt(); + QByteArray fileName = child.findChild("file").data(); + if (!fileLoaded) { + QFile file(QFile::decodeName(fileName)); + file.open(QIODevice::ReadOnly); + fileContents = file.readAll().split('\n'); + fileLoaded = true; + } + if (line >= 0 && line < fileContents.size()) + ba += " " + fileContents.at(line) + '\n'; + + GdbMi insn = child.findChild("line_asm_insn"); + foreach (const GdbMi &line, insn.children()) + ba += parseLine(line); + } else { + // the non-mixed version + ba += parseLine(child); + } + } + return _(ba); } +void GdbEngine::handleFetchDisassemblerByLine(const GdbResultRecord &record, + const QVariant &cookie) +{ + bool ok = true; + DisassemblerViewAgent *agent = (DisassemblerViewAgent *)cookie.toULongLong(&ok); + QTC_ASSERT(agent, return); + + if (record.resultClass == GdbResultDone) { + GdbMi lines = record.data.findChild("asm_insns"); + if (lines.children().isEmpty()) + fetchDisassemblerByAddress(agent, true); + else + agent->setContents(parseDisassembler(lines)); + } else if (record.resultClass == GdbResultError) { + //536^error,msg="mi_cmd_disassemble: Invalid line number" + QByteArray msg = record.data.findChild("msg").data(); + if (msg == "mi_cmd_disassemble: Invalid line number") + fetchDisassemblerByAddress(agent, true); + } +} + +void GdbEngine::handleFetchDisassemblerByAddress1(const GdbResultRecord &record, + const QVariant &cookie) +{ + bool ok = true; + DisassemblerViewAgent *agent = (DisassemblerViewAgent *)cookie.toULongLong(&ok); + QTC_ASSERT(agent, return); + + if (record.resultClass == GdbResultDone) { + GdbMi lines = record.data.findChild("asm_insns"); + if (lines.children().isEmpty()) + fetchDisassemblerByAddress(agent, false); + else + agent->setContents(parseDisassembler(lines)); + } +} + +void GdbEngine::handleFetchDisassemblerByAddress0(const GdbResultRecord &record, + const QVariant &cookie) +{ + bool ok = true; + DisassemblerViewAgent *agent = (DisassemblerViewAgent *)cookie.toULongLong(&ok); + QTC_ASSERT(agent, return); + + if (record.resultClass == GdbResultDone) { + GdbMi lines = record.data.findChild("asm_insns"); + agent->setContents(parseDisassembler(lines)); + } +} + + IDebuggerEngine *createGdbEngine(DebuggerManager *parent, QList<Core::IOptionsPage*> *opts) { opts->push_back(new GdbOptionsPage); diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index 367b94856ce..3f16bd3701c 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -125,6 +125,17 @@ private: void fetchMemory(MemoryViewAgent *agent, quint64 addr, quint64 length); void handleFetchMemory(const GdbResultRecord &record, const QVariant &cookie); + void fetchDisassembler(DisassemblerViewAgent *agent, + const StackFrame &frame); + void fetchDisassemblerByAddress(DisassemblerViewAgent *agent, + bool useMixedMode); + void handleFetchDisassemblerByLine(const GdbResultRecord &record, + const QVariant &cookie); + void handleFetchDisassemblerByAddress1(const GdbResultRecord &record, + const QVariant &cookie); + void handleFetchDisassemblerByAddress0(const GdbResultRecord &record, + const QVariant &cookie); + 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 ad16fcec701..16b6d5ace5a 100644 --- a/src/plugins/debugger/idebuggerengine.h +++ b/src/plugins/debugger/idebuggerengine.h @@ -46,10 +46,12 @@ class ITextEditor; namespace Debugger { namespace Internal { +class DebuggerStartParameters; +class DisassemblerViewAgent; +class MemoryViewAgent; +class StackFrame; class Symbol; class WatchData; -struct DebuggerStartParameters; -class MemoryViewAgent; class IDebuggerEngine : public QObject { @@ -98,6 +100,8 @@ public: virtual void watchPoint(const QPoint &) {} virtual void fetchMemory(MemoryViewAgent *, quint64 addr, quint64 length) { Q_UNUSED(addr); Q_UNUSED(length); } + virtual void fetchDisassembler(DisassemblerViewAgent *, const StackFrame &frame) + { Q_UNUSED(frame); } }; } // namespace Internal diff --git a/src/plugins/debugger/registerhandler.cpp b/src/plugins/debugger/registerhandler.cpp index 8639261b594..a4cd71d394f 100644 --- a/src/plugins/debugger/registerhandler.cpp +++ b/src/plugins/debugger/registerhandler.cpp @@ -76,6 +76,12 @@ QVariant RegisterHandler::data(const QModelIndex &index, int role) const const Register ® = m_registers.at(index.row()); + if (role == Qt::UserRole + 1) { + bool ok = true; + qulonglong value = reg.value.toULongLong(&ok, 0); + return QString::fromLatin1("0x") + QString::number(value, 16); + } + const QString padding = " "; if (role == Qt::DisplayRole) { switch (index.column()) { diff --git a/src/plugins/debugger/registerwindow.cpp b/src/plugins/debugger/registerwindow.cpp index 11a0340b6fa..2a8fd344373 100644 --- a/src/plugins/debugger/registerwindow.cpp +++ b/src/plugins/debugger/registerwindow.cpp @@ -30,6 +30,7 @@ #include "registerwindow.h" #include "debuggeractions.h" +#include "debuggeragents.h" #include "debuggerconstants.h" #include <QtCore/QDebug> @@ -47,8 +48,9 @@ using namespace Debugger::Internal; using namespace Debugger::Constants; -RegisterWindow::RegisterWindow() - : m_alwaysResizeColumnsToContents(true), m_alwaysReloadContents(false) +RegisterWindow::RegisterWindow(DebuggerManager *manager) + : m_manager(manager), m_alwaysResizeColumnsToContents(true), + m_alwaysReloadContents(false) { QAction *act = theDebuggerAction(UseAlternatingRowColors); setWindowTitle(tr("Registers")); @@ -81,6 +83,17 @@ void RegisterWindow::contextMenuEvent(QContextMenuEvent *ev) actAlwaysReload->setChecked(m_alwaysReloadContents); menu.addSeparator(); + QModelIndex idx = indexAt(ev->pos()); + QString address = model()->data(idx, Qt::UserRole + 1).toString(); + QAction *actShowMemory = menu.addAction(QString()); + if (address.isEmpty()) { + actShowMemory->setText(tr("Open memory editor")); + actShowMemory->setEnabled(false); + } else { + actShowMemory->setText(tr("Open memory editor at %1").arg(address)); + } + menu.addSeparator(); + int base = model()->data(QModelIndex(), Qt::UserRole).toInt(); QAction *act16 = menu.addAction(tr("Hexadecimal")); act16->setCheckable(true); @@ -108,6 +121,8 @@ void RegisterWindow::contextMenuEvent(QContextMenuEvent *ev) reloadContents(); else if (act == actAlwaysReload) setAlwaysReloadContents(!m_alwaysReloadContents); + else if (act == actShowMemory) + (void) new MemoryViewAgent(m_manager, address); else if (act) { base = (act == act10 ? 10 : act == act8 ? 8 : act == act2 ? 2 : 16); QMetaObject::invokeMethod(model(), "setNumberBase", Q_ARG(int, base)); diff --git a/src/plugins/debugger/registerwindow.h b/src/plugins/debugger/registerwindow.h index 4839c796137..f29283f3b54 100644 --- a/src/plugins/debugger/registerwindow.h +++ b/src/plugins/debugger/registerwindow.h @@ -35,12 +35,14 @@ namespace Debugger { namespace Internal { +class DebuggerManager; + class RegisterWindow : public QTreeView { Q_OBJECT public: - RegisterWindow(); + explicit RegisterWindow(DebuggerManager *manager); void setModel(QAbstractItemModel *model); signals: @@ -57,6 +59,8 @@ private: void resizeEvent(QResizeEvent *ev); void contextMenuEvent(QContextMenuEvent *ev); + DebuggerManager *m_manager; + bool m_alwaysResizeColumnsToContents; bool m_alwaysReloadContents; }; diff --git a/src/plugins/debugger/script/scriptengine.cpp b/src/plugins/debugger/script/scriptengine.cpp index a6f099ac9a3..44a9f610fc1 100644 --- a/src/plugins/debugger/script/scriptengine.cpp +++ b/src/plugins/debugger/script/scriptengine.cpp @@ -565,7 +565,10 @@ void ScriptEngine::maybeBreakNow(bool byFunction) } qq->notifyInferiorStopped(); - q->gotoLocation(fileName, lineNumber, true); + StackFrame frame; + frame.file = fileName; + frame.line = lineNumber; + q->gotoLocation(frame, true); updateLocals(); } diff --git a/src/plugins/debugger/stackframe.h b/src/plugins/debugger/stackframe.h new file mode 100644 index 00000000000..35108aef3ef --- /dev/null +++ b/src/plugins/debugger/stackframe.h @@ -0,0 +1,60 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DEBUGGER_STACKFRAME_H +#define DEBUGGER_STACKFRAME_H + +#include <QtCore/QString> +#include <QtCore/QVariant> + +namespace Debugger { +namespace Internal { + +struct StackFrame +{ + StackFrame(int level = 0); + bool isUsable() const; + QString toToolTip() const; + QString toString() const; + + int level; + QString function; + QString file; // we try to put an absolute file name in there + QString from; + QString to; + int line; + QString address; +}; + +} // namespace Internal +} // namespace Debugger + +Q_DECLARE_METATYPE(Debugger::Internal::StackFrame); + +#endif // DEBUGGER_STACKFRAME_H diff --git a/src/plugins/debugger/stackhandler.cpp b/src/plugins/debugger/stackhandler.cpp index c315e4f9b64..e5bd3879f64 100644 --- a/src/plugins/debugger/stackhandler.cpp +++ b/src/plugins/debugger/stackhandler.cpp @@ -29,6 +29,8 @@ #include "stackhandler.h" +#include "debuggeractions.h" + #include <utils/qtcassert.h> #include <QtCore/QAbstractTableModel> @@ -46,6 +48,19 @@ bool StackFrame::isUsable() const return !file.isEmpty() && QFileInfo(file).isReadable(); } +QString StackFrame::toString() const +{ + QString res; + QTextStream str(&res); + str << StackHandler::tr("Address:") << " " << address << " " + << StackHandler::tr("Function:") << " " << function << " " + << StackHandler::tr("File:") << " " << file << " " + << StackHandler::tr("Line:") << " " << line << " " + << StackHandler::tr("From:") << " " << from << " " + << StackHandler::tr("To:") << " " << to; + return res; +} + QString StackFrame::toToolTip() const { QString res; @@ -117,14 +132,22 @@ QVariant StackHandler::data(const QModelIndex &index, int role) const case 4: // Address return frame.address; } - } else if (role == Qt::ToolTipRole) { + return QVariant(); + } + + if (role == Qt::ToolTipRole) { //: Tooltip for variable return frame.toToolTip(); - } else if (role == Qt::DecorationRole && index.column() == 0) { + } + + if (role == Qt::DecorationRole && index.column() == 0) { // Return icon that indicates whether this is the active stack frame return (index.row() == m_currentIndex) ? m_positionIcon : m_emptyIcon; } + if (role == Qt::UserRole) + return QVariant::fromValue(frame); + return QVariant(); } @@ -149,7 +172,8 @@ Qt::ItemFlags StackHandler::flags(const QModelIndex &index) const if (index.row() == m_stackFrames.size()) return QAbstractTableModel::flags(index); const StackFrame &frame = m_stackFrames.at(index.row()); - const bool isValid = (!frame.file.isEmpty() && !frame.function.isEmpty()); + const bool isValid = (!frame.file.isEmpty() && !frame.function.isEmpty()) + || theDebuggerBoolSetting(StepByInstruction); return isValid ? QAbstractTableModel::flags(index) : Qt::ItemFlags(0); } diff --git a/src/plugins/debugger/stackhandler.h b/src/plugins/debugger/stackhandler.h index 63f49e6c525..3fee2a5f872 100644 --- a/src/plugins/debugger/stackhandler.h +++ b/src/plugins/debugger/stackhandler.h @@ -30,11 +30,14 @@ #ifndef DEBUGGER_STACKHANDLER_H #define DEBUGGER_STACKHANDLER_H +#include "stackframe.h" + #include <QtCore/QAbstractTableModel> #include <QtCore/QObject> #include <QtGui/QIcon> + namespace Debugger { namespace Internal { @@ -44,21 +47,6 @@ namespace Internal { // //////////////////////////////////////////////////////////////////////// -struct StackFrame -{ - StackFrame(int level = 0); - bool isUsable() const; - QString toToolTip() const; - - int level; - QString function; - QString file; // we try to put an absolute file name in there - QString from; - QString to; - int line; - QString address; -}; - /*! A model to represent the stack in a QTreeView. */ class StackHandler : public QAbstractTableModel { diff --git a/src/plugins/debugger/stackwindow.cpp b/src/plugins/debugger/stackwindow.cpp index 2bb9d721b7b..1f397b5114f 100644 --- a/src/plugins/debugger/stackwindow.cpp +++ b/src/plugins/debugger/stackwindow.cpp @@ -28,8 +28,10 @@ **************************************************************************/ #include "stackwindow.h" +#include "stackframe.h" #include "debuggeractions.h" +#include "debuggeragents.h" #include <utils/qtcassert.h> @@ -46,11 +48,14 @@ #include <QtGui/QVBoxLayout> -using Debugger::Internal::StackWindow; +namespace Debugger { +namespace Internal { -StackWindow::StackWindow(QWidget *parent) - : QTreeView(parent), m_alwaysResizeColumnsToContents(false) +StackWindow::StackWindow(DebuggerManager *manager, QWidget *parent) + : QTreeView(parent), m_manager(manager), m_alwaysResizeColumnsToContents(false) { + m_disassemblerAgent = new DisassemblerViewAgent(manager); + QAction *act = theDebuggerAction(UseAlternatingRowColors); setWindowTitle(tr("Stack")); @@ -90,33 +95,60 @@ void StackWindow::rowActivated(const QModelIndex &index) void StackWindow::contextMenuEvent(QContextMenuEvent *ev) { + QModelIndex idx = indexAt(ev->pos()); + StackFrame frame = model()->data(idx, Qt::UserRole).value<StackFrame>(); + QString address = frame.address; + + qDebug() << "RECV: " << frame.toToolTip(); + QMenu menu; - QAction *act0 = new QAction(tr("Copy contents to clipboard"), &menu); - act0->setEnabled(model() != 0); + QAction *actAdjust = menu.addAction(tr("Adjust column widths to contents")); - QAction *act1 = new QAction(tr("Adjust column widths to contents"), &menu); + QAction *actAlwaysAdjust = + menu.addAction(tr("Always adjust column widths to contents")); + actAlwaysAdjust->setCheckable(true); + actAlwaysAdjust->setChecked(m_alwaysResizeColumnsToContents); - QAction *act2 = new QAction(tr("Always adjust column widths to contents"), &menu); - act2->setCheckable(true); - act2->setChecked(m_alwaysResizeColumnsToContents); + menu.addSeparator(); menu.addAction(theDebuggerAction(ExpandStack)); - menu.addAction(act0); - menu.addSeparator(); - menu.addAction(act1); - menu.addAction(act2); + + QAction *actCopyContents = menu.addAction(tr("Copy contents to clipboard")); + actCopyContents->setEnabled(model() != 0); + + QAction *actShowMemory = menu.addAction(QString()); + if (address.isEmpty()) { + actShowMemory->setText(tr("Open memory editor")); + actShowMemory->setEnabled(false); + } else { + actShowMemory->setText(tr("Open memory editor at %1").arg(address)); + } + + QAction *actShowDisassembler = menu.addAction(QString()); + if (address.isEmpty()) { + actShowDisassembler->setText(tr("Open disassembler")); + actShowDisassembler->setEnabled(false); + } else { + actShowDisassembler->setText(tr("Open disassembler at %1").arg(address)); + } + menu.addSeparator(); + menu.addAction(theDebuggerAction(SettingsDialog)); QAction *act = menu.exec(ev->globalPos()); - if (act == act0) + if (act == actCopyContents) copyContentsToClipboard(); - else if (act == act1) + else if (act == actAdjust) resizeColumnsToContents(); - else if (act == act2) + else if (act == actAlwaysAdjust) setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents); + else if (act == actShowMemory) + (void) new MemoryViewAgent(m_manager, address); + else if (act == actShowDisassembler) + m_disassemblerAgent->setFrame(frame); } void StackWindow::copyContentsToClipboard() @@ -157,3 +189,6 @@ void StackWindow::setAlwaysResizeColumnsToContents(bool on) header()->setResizeMode(2, mode); header()->setResizeMode(3, mode); } + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/stackwindow.h b/src/plugins/debugger/stackwindow.h index 42bc03749ce..2703735420e 100644 --- a/src/plugins/debugger/stackwindow.h +++ b/src/plugins/debugger/stackwindow.h @@ -40,13 +40,16 @@ QT_END_NAMESPACE namespace Debugger { namespace Internal { + +class DebuggerManager; +class DisassemblerViewAgent; class StackWindow : public QTreeView { Q_OBJECT public: - StackWindow(QWidget *parent = 0); + StackWindow(DebuggerManager *manager, QWidget *parent = 0); signals: void frameActivated(int); @@ -64,6 +67,8 @@ private: void contextMenuEvent(QContextMenuEvent *ev); void copyContentsToClipboard(); + DebuggerManager *m_manager; + DisassemblerViewAgent *m_disassemblerAgent; bool m_alwaysResizeColumnsToContents; }; diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp index 6d090bf585a..a41c6721a9a 100644 --- a/src/plugins/debugger/watchwindow.cpp +++ b/src/plugins/debugger/watchwindow.cpp @@ -297,9 +297,7 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev) theDebuggerAction(WatchExpression) ->trigger(WatchHandler::watcherEditPlaceHolder()); } else if (act == actWatchKnownMemory) { - bool ok = true; - uint addr = address.toUInt(&ok, 0); - (void) new MemoryViewAgent(m_manager, addr); + (void) new MemoryViewAgent(m_manager, address); } else if (act == actWatchUnknownMemory) { QLabel *label = new QLabel("Enter an address: "); QLineEdit *lineEdit = new QLineEdit; @@ -310,11 +308,8 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev) dialog.setWindowTitle("Select start address"); dialog.setLayout(layout); connect(lineEdit, SIGNAL(returnPressed()), &dialog, SLOT(accept())); - if (dialog.exec() == QDialog::Accepted) { - bool ok = true; - uint addr = lineEdit->text().toUInt(&ok, 0); - (void) new MemoryViewAgent(m_manager, addr); - } + if (dialog.exec() == QDialog::Accepted) + (void) new MemoryViewAgent(m_manager, address); } else if (act == actSelectWidgetToWatch) { grabMouse(Qt::CrossCursor); m_grabbing = true; -- GitLab