diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro index e77b89d19a41989903a6cd057921711e0a4594ea..8f7e909c3185f9d8f3b953e60072ef91c85826eb 100644 --- a/src/plugins/debugger/debugger.pro +++ b/src/plugins/debugger/debugger.pro @@ -64,7 +64,7 @@ HEADERS += breakhandler.h \ debuggertooltipmanager.h \ debuggertoolchaincombobox.h \ debuggersourcepathmappingwidget.h \ - memoryviewwidget.h + memoryview.h SOURCES += breakhandler.cpp \ breakpoint.cpp \ @@ -108,7 +108,7 @@ SOURCES += breakhandler.cpp \ debuggertooltipmanager.cpp \ debuggertoolchaincombobox.cpp \ debuggersourcepathmappingwidget.cpp \ - memoryviewwidget.cpp + memoryview.cpp FORMS += attachexternaldialog.ui \ attachcoredialog.ui \ diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 87e410351734c0bead160dbeec1ff35477c04f60..f336d7de9d2d5c78c9af1ce67f25ecffbf54f58a 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -1569,14 +1569,11 @@ bool DebuggerEngine::isCppBreakpoint(const BreakpointParameters &p) && !p.fileName.endsWith(QLatin1String(".js"), Qt::CaseInsensitive); } -void DebuggerEngine::openMemoryView(quint64 address) +void DebuggerEngine::openMemoryView(quint64 startAddr, unsigned flags, + const QList<MemoryMarkup> &ml, const QPoint &pos, + const QString &title, QWidget *parent) { - d->m_memoryAgent.createBinEditor(address); -} - -void DebuggerEngine::addMemoryView(Internal::MemoryViewWidget *w) -{ - d->m_memoryAgent.addMemoryView(w); + d->m_memoryAgent.createBinEditor(startAddr, flags, ml, pos, title, parent); } void DebuggerEngine::updateMemoryViews() diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index a717cfd068fb4b92d955d931bcc5d8a21265724b..423a667ee30a5b67875a968bbf2e8e1bb9473dc3 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -81,7 +81,7 @@ class WatchHandler; class BreakpointParameters; class QmlCppEngine; class DebuggerToolTipContext; -class MemoryViewWidget; +class MemoryMarkup; struct WatchUpdateFlags { @@ -156,8 +156,18 @@ public: virtual void startDebugger(DebuggerRunControl *runControl); virtual void watchPoint(const QPoint &); - virtual void openMemoryView(quint64 addr); - virtual void addMemoryView(Internal::MemoryViewWidget *w); + + enum MemoryViewFlags + { + MemoryReadOnly = 0x1, //!< Read-only. + MemoryTrackRegister = 0x2, //!< Address parameter is register number to track + MemoryView = 0x4 //!< Open a separate view (using the pos-parameter). + }; + + virtual void openMemoryView(quint64 startAddr, unsigned flags, + const QList<Internal::MemoryMarkup> &ml, + const QPoint &pos, + const QString &title = QString(), QWidget *parent = 0); virtual void fetchMemory(Internal::MemoryAgent *, QObject *, quint64 addr, quint64 length); virtual void changeMemory(Internal::MemoryAgent *, QObject *, diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 4f6cce47d4e4af4e863ce458fd1de69e6430527c..1628b3df5714382e1c55e7e324c59b0fc3199041 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -42,7 +42,7 @@ #include "debuggerrunner.h" #include "debuggerruncontrolfactory.h" #include "debuggerstringutils.h" - +#include "memoryagent.h" #include "breakpoint.h" #include "breakhandler.h" #include "breakwindow.h" @@ -2184,7 +2184,7 @@ void DebuggerPluginPrivate::openMemoryEditor() { AddressDialog dialog; if (dialog.exec() == QDialog::Accepted) - currentEngine()->openMemoryView(dialog.address()); + currentEngine()->openMemoryView(dialog.address(), 0, QList<MemoryMarkup>(), QPoint()); } void DebuggerPluginPrivate::coreShutdown() diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 51c5c17c3bcfd1333c48a1ebe2adb465c1ef1ad2..81b5c88e08915d2e852190ffde3aed9d9afa950d 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -3794,10 +3794,8 @@ void GdbEngine::updateLocals(const QVariant &cookie) updateLocalsPython(false, QByteArray()); else updateLocalsClassic(cookie); - updateMemoryViews(); } - // Parse a local variable from GdbMi. WatchData GdbEngine::localVariable(const GdbMi &item, const QStringList &uninitializedVariables, diff --git a/src/plugins/debugger/memoryagent.cpp b/src/plugins/debugger/memoryagent.cpp index 08cdce27ba0c1a384c141062ba8dd0a0b57d7def..0639193e5f04f2027cc910c637271c7c9ede731f 100644 --- a/src/plugins/debugger/memoryagent.cpp +++ b/src/plugins/debugger/memoryagent.cpp @@ -31,11 +31,11 @@ **************************************************************************/ #include "memoryagent.h" +#include "memoryview.h" #include "debuggerengine.h" #include "debuggerstartparameters.h" #include "debuggercore.h" -#include "memoryviewwidget.h" #include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/editormanager.h> @@ -43,9 +43,12 @@ #include <coreplugin/icore.h> #include <utils/qtcassert.h> +#include <extensionsystem/pluginmanager.h> +#include <extensionsystem/invoker.h> #include <QtGui/QMessageBox> #include <QtGui/QMainWindow> +#include <QtGui/QVBoxLayout> #include <cstring> @@ -66,6 +69,22 @@ namespace Internal { 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. + + Memory can be shown as + \list + \o Editor: Create an IEditor using the normal editor factory + interface (m_editors) + \o View: Separate top-level view consisting of a Bin Editor widget + (m_view). + \endlist + + Views are updated on the DebuggerEngine::stackFrameCompleted signal. + An exception are views of class Debugger::RegisterMemoryView tracking the + content pointed to by a register (eg stack pointer, instruction pointer). + They are connected to the set/changed signals of + the engine's register handler. + + \sa Debugger::MemoryView, Debugger::RegisterMemoryView */ namespace { const int DataRange = 1024 * 1024; } @@ -74,142 +93,193 @@ MemoryAgent::MemoryAgent(DebuggerEngine *engine) : QObject(engine), m_engine(engine) { QTC_ASSERT(engine, /**/); + connect(engine, SIGNAL(stateChanged(Debugger::DebuggerState)), + this, SLOT(engineStateChanged(Debugger::DebuggerState))); + connect(engine, SIGNAL(stackFrameCompleted()), this, + SLOT(updateContents())); } MemoryAgent::~MemoryAgent() { + closeEditors(); + closeViews(); +} + +void MemoryAgent::closeEditors() +{ + if (m_editors.isEmpty()) + return; + QList<IEditor *> editors; foreach (QPointer<IEditor> editor, m_editors) if (editor) editors.append(editor.data()); EditorManager::instance()->closeEditors(editors); + m_editors.clear(); } -void MemoryAgent::openMemoryView(quint64 address, quint64 length, const QPoint &pos) +void MemoryAgent::closeViews() { - MemoryViewWidget *w = new MemoryViewWidget(Core::ICore::instance()->mainWindow()); - w->setUpdateOnInferiorStop(true); - w->move(pos); - w->requestMemory(address, length); - addMemoryView(w); + foreach (const QPointer<MemoryView> &w, m_views) + if (w) + w->close(); + m_views.clear(); } -void MemoryAgent::addMemoryView(MemoryViewWidget *w) +void MemoryAgent::updateMemoryView(quint64 address, quint64 length) { - w->setAbi(m_engine->startParameters().toolChainAbi); - connect(w, SIGNAL(memoryRequested(quint64,quint64)), - this, SLOT(updateMemoryView(quint64,quint64))); - connect(m_engine, SIGNAL(stateChanged(Debugger::DebuggerState)), - w, SLOT(engineStateChanged(Debugger::DebuggerState))); - connect(w, SIGNAL(openViewRequested(quint64,quint64,QPoint)), - this, SLOT(openMemoryView(quint64,quint64,QPoint))); - w->requestMemory(); - w->show(); + m_engine->fetchMemory(this, sender(), address, length); } -void MemoryAgent::updateMemoryView(quint64 address, quint64 length) +void MemoryAgent::connectBinEditorWidget(QWidget *w) { - m_engine->fetchMemory(this, sender(), address, length); + connect(w, + SIGNAL(dataRequested(Core::IEditor*,quint64)), + SLOT(fetchLazyData(Core::IEditor*,quint64))); + connect(w, + SIGNAL(newWindowRequested(quint64)), + SLOT(createBinEditor(quint64))); + connect(w, + SIGNAL(newRangeRequested(Core::IEditor*,quint64)), + SLOT(provideNewRange(Core::IEditor*,quint64))); + connect(w, + SIGNAL(startOfFileRequested(Core::IEditor*)), + SLOT(handleStartOfFileRequested(Core::IEditor*))); + connect(w, + SIGNAL(endOfFileRequested(Core::IEditor *)), + SLOT(handleEndOfFileRequested(Core::IEditor*))); + connect(w, + SIGNAL(dataChanged(Core::IEditor*,quint64,QByteArray)), + SLOT(handleDataChanged(Core::IEditor*,quint64,QByteArray))); } -void MemoryAgent::createBinEditor(quint64 addr) +bool MemoryAgent::doCreateBinEditor(quint64 addr, unsigned flags, + const QList<MemoryMarkup> &ml, const QPoint &pos, + QString title, QWidget *parent) { + const bool readOnly = (flags & DebuggerEngine::MemoryReadOnly) != 0; + if (title.isEmpty()) + title = tr("Memory at 0x%1").arg(addr, 0, 16); + // Separate view? + if (flags & DebuggerEngine::MemoryView) { + // Ask BIN editor plugin for factory service and have it create a bin editor widget. + QWidget *binEditor = 0; + if (QObject *factory = ExtensionSystem::PluginManager::instance()->getObjectByClassName("BINEditor::BinEditorWidgetFactory")) + binEditor = ExtensionSystem::invoke<QWidget *>(factory, "createWidget", (QWidget *)0); + if (!binEditor) + return false; + connectBinEditorWidget(binEditor); + MemoryView::setBinEditorReadOnly(binEditor, readOnly); + MemoryView::setBinEditorNewWindowRequestAllowed(binEditor, true); + MemoryView *topLevel = 0; + // Memory view tracking register value, providing its own updating mechanism. + if (flags & DebuggerEngine::MemoryTrackRegister) { + RegisterMemoryView *rmv = new RegisterMemoryView(binEditor, parent); + rmv->init(m_engine->registerHandler(), int(addr)); + topLevel = rmv; + } else { + // Ordinary memory view + MemoryView::setBinEditorMarkup(binEditor, ml); + MemoryView::setBinEditorRange(binEditor, addr, MemoryAgent::DataRange, MemoryAgent::BinBlockSize); + topLevel = new MemoryView(binEditor, parent); + topLevel->setWindowTitle(title); + } + m_views << topLevel; + topLevel->move(pos); + topLevel->show(); + return true; + } + // Editor: Register tracking not supported. + QTC_ASSERT(!(flags & DebuggerEngine::MemoryTrackRegister), return false; ) EditorManager *editorManager = EditorManager::instance(); - QString titlePattern = tr("Memory $"); + if (!title.endsWith(QLatin1Char('$'))) + title.append(QLatin1String(" $")); IEditor *editor = editorManager->openEditorWithContents( - Core::Constants::K_DEFAULT_BINARY_EDITOR_ID, - &titlePattern); - if (editor) { - editor->setProperty(Constants::OPENED_BY_DEBUGGER, true); - editor->setProperty(Constants::OPENED_WITH_MEMORY, true); - connect(editor->widget(), - SIGNAL(dataRequested(Core::IEditor*,quint64)), - SLOT(fetchLazyData(Core::IEditor*,quint64))); - connect(editor->widget(), - SIGNAL(newWindowRequested(quint64)), - SLOT(createBinEditor(quint64))); - connect(editor->widget(), - SIGNAL(newRangeRequested(Core::IEditor*,quint64)), - SLOT(provideNewRange(Core::IEditor*,quint64))); - connect(editor->widget(), - SIGNAL(startOfFileRequested(Core::IEditor*)), - SLOT(handleStartOfFileRequested(Core::IEditor*))); - connect(editor->widget(), - SIGNAL(endOfFileRequested(Core::IEditor *)), - SLOT(handleEndOfFileRequested(Core::IEditor*))); - connect(editor->widget(), - SIGNAL(dataChanged(Core::IEditor*,quint64,QByteArray)), - SLOT(handleDataChanged(Core::IEditor*,quint64,QByteArray))); - m_editors << editor; - QMetaObject::invokeMethod(editor->widget(), "setNewWindowRequestAllowed"); - QMetaObject::invokeMethod(editor->widget(), "setSizes", - Q_ARG(quint64, addr), - Q_ARG(int, DataRange), - Q_ARG(int, BinBlockSize)); - editorManager->activateEditor(editor); - } else { + Core::Constants::K_DEFAULT_BINARY_EDITOR_ID, &title); + if (!editor) + return false; + editor->setProperty(Constants::OPENED_BY_DEBUGGER, QVariant(true)); + editor->setProperty(Constants::OPENED_WITH_MEMORY, QVariant(true)); + QWidget *editorBinEditor = editor->widget(); + connectBinEditorWidget(editorBinEditor); + MemoryView::setBinEditorReadOnly(editorBinEditor, readOnly); + MemoryView::setBinEditorNewWindowRequestAllowed(editorBinEditor, true); + MemoryView::setBinEditorRange(editorBinEditor, addr, MemoryAgent::DataRange, MemoryAgent::BinBlockSize); + MemoryView::setBinEditorMarkup(editorBinEditor, ml); + m_editors << editor; + editorManager->activateEditor(editor); + return true; +} + +void MemoryAgent::createBinEditor(quint64 addr, unsigned flags, + const QList<MemoryMarkup> &ml, const QPoint &pos, + const QString &title, QWidget *parent) +{ + if (!doCreateBinEditor(addr, flags, ml, pos, title, parent)) showMessageBox(QMessageBox::Warning, - tr("No memory viewer available"), + tr("No Memory Viewer Available"), tr("The memory contents cannot be shown as no viewer plugin " "for binary data has been loaded.")); - deleteLater(); - } } -void MemoryAgent::fetchLazyData(IEditor *editor, quint64 block) +void MemoryAgent::createBinEditor(quint64 addr) { - m_engine->fetchMemory(this, editor, BinBlockSize * block, BinBlockSize); + createBinEditor(addr, 0, QList<MemoryMarkup>(), QPoint(), QString(), 0); +} + +void MemoryAgent::fetchLazyData(IEditor *, quint64 block) +{ + m_engine->fetchMemory(this, sender(), BinBlockSize * block, BinBlockSize); } void MemoryAgent::addLazyData(QObject *editorToken, quint64 addr, const QByteArray &ba) { - - if (IEditor *editor = qobject_cast<IEditor *>(editorToken)) { - if (QWidget *editorWidget = editor->widget()) { - QMetaObject::invokeMethod(editorWidget , "addData", - Q_ARG(quint64, addr / BinBlockSize), Q_ARG(QByteArray, ba)); - } - return; - } - if (MemoryViewWidget *mvw = qobject_cast<MemoryViewWidget*>(editorToken)) - mvw->setData(ba); + QWidget *w = qobject_cast<QWidget *>(editorToken); + QTC_ASSERT(w, return ;) + MemoryView::binEditorAddData(w, addr, ba); } -void MemoryAgent::provideNewRange(IEditor *editor, quint64 address) +void MemoryAgent::provideNewRange(IEditor *, quint64 address) { - QMetaObject::invokeMethod(editor->widget(), "setSizes", - Q_ARG(quint64, address), - Q_ARG(int, DataRange), - Q_ARG(int, BinBlockSize)); + QWidget *w = qobject_cast<QWidget *>(sender()); + QTC_ASSERT(w, return ;) + MemoryView::setBinEditorRange(w, address, DataRange, BinBlockSize); } // Since we are not dealing with files, we take these signals to mean // "move to start/end of range". This seems to make more sense than // jumping to the start or end of the address space, respectively. -void MemoryAgent::handleStartOfFileRequested(IEditor *editor) +void MemoryAgent::handleStartOfFileRequested(IEditor *) { - QMetaObject::invokeMethod(editor->widget(), - "setCursorPosition", Q_ARG(int, 0)); + QWidget *w = qobject_cast<QWidget *>(sender()); + QTC_ASSERT(w, return ;) + MemoryView::binEditorSetCursorPosition(w, 0); } -void MemoryAgent::handleEndOfFileRequested(IEditor *editor) +void MemoryAgent::handleEndOfFileRequested(IEditor *) { - QMetaObject::invokeMethod(editor->widget(), - "setCursorPosition", Q_ARG(int, DataRange - 1)); + QWidget *w = qobject_cast<QWidget *>(sender()); + QTC_ASSERT(w, return ;) + MemoryView::binEditorSetCursorPosition(w, DataRange - 1); } -void MemoryAgent::handleDataChanged(IEditor *editor, +void MemoryAgent::handleDataChanged(IEditor *, quint64 addr, const QByteArray &data) { - m_engine->changeMemory(this, editor, addr, data); + m_engine->changeMemory(this, sender(), addr, data); } void MemoryAgent::updateContents() { - foreach (QPointer<IEditor> editor, m_editors) - if (editor) - QMetaObject::invokeMethod(editor->widget(), "updateContents"); + foreach (const QPointer<Core::IEditor> &e, m_editors) + if (e) + MemoryView::binEditorUpdateContents(e->widget()); + // Update all views except register views, which trigger on + // register value set/changed. + foreach (const QPointer<MemoryView> &w, m_views) + if (w && !qobject_cast<RegisterMemoryView *>(w.data())) + w->updateContents(); } bool MemoryAgent::hasVisibleEditor() const @@ -221,6 +291,22 @@ bool MemoryAgent::hasVisibleEditor() const return false; } +void MemoryAgent::engineStateChanged(Debugger::DebuggerState s) +{ + switch (s) { + case DebuggerFinished: + closeViews(); + foreach (const QPointer<IEditor> &editor, m_editors) + if (editor) { // Prevent triggering updates, etc. + MemoryView::setBinEditorReadOnly(editor->widget(), true); + editor->widget()->disconnect(this); + } + break; + default: + break; + } +} + bool MemoryAgent::isBigEndian(const ProjectExplorer::Abi &a) { switch (a.architecture()) { @@ -260,3 +346,4 @@ quint64 MemoryAgent::readInferiorPointerValue(const unsigned char *data, const P } // namespace Internal } // namespace Debugger +; diff --git a/src/plugins/debugger/memoryagent.h b/src/plugins/debugger/memoryagent.h index 7d005cfe8c85776dff76a2c9e55aad761188ffca..5cd78511ab02c486413422712ac57122abd0df98 100644 --- a/src/plugins/debugger/memoryagent.h +++ b/src/plugins/debugger/memoryagent.h @@ -33,8 +33,13 @@ #ifndef DEBUGGER_MEMORYAGENT_H #define DEBUGGER_MEMORYAGENT_H +#include "debuggerconstants.h" + #include <QtCore/QObject> #include <QtCore/QPointer> +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtGui/QColor> QT_FORWARD_DECLARE_CLASS(QPoint) @@ -51,7 +56,19 @@ namespace Debugger { class DebuggerEngine; namespace Internal { -class MemoryViewWidget; +class MemoryView; +class MemoryMarkup +{ +public: + MemoryMarkup(quint64 a = 0, quint64 l = 0, QColor c = Qt::yellow, + const QString &tt = QString()) : + address(a), length(l), color(c), toolTip(tt) {} + + quint64 address; + quint64 length; + QColor color; + QString toolTip; +}; class MemoryAgent : public QObject { @@ -62,6 +79,8 @@ public: ~MemoryAgent(); enum { BinBlockSize = 1024 }; + enum { DataRange = 1024 * 1024 }; + bool hasVisibleEditor() const; static bool isBigEndian(const ProjectExplorer::Abi &a); @@ -69,13 +88,16 @@ public: public slots: // Called by engine to create a new view. + void createBinEditor(quint64 startAddr, unsigned flags, + const QList<MemoryMarkup> &ml, const QPoint &pos, + const QString &title, QWidget *parent); void createBinEditor(quint64 startAddr); // Called by engine to create a tooltip. - void addMemoryView(MemoryViewWidget *w); - // Called by engine to trigger update of contents. - void updateContents(); - // Called by engine to pass updated contents. void addLazyData(QObject *editorToken, quint64 addr, const QByteArray &data); + // On stack frame completed and on request. + void updateContents(); + void closeEditors(); + void closeViews(); private slots: void fetchLazyData(Core::IEditor *, quint64 block); @@ -85,10 +107,16 @@ private slots: void handleDataChanged(Core::IEditor *editor, quint64 address, const QByteArray &data); void updateMemoryView(quint64 address, quint64 length); - void openMemoryView(quint64 address, quint64 length, const QPoint &pos); + void engineStateChanged(Debugger::DebuggerState s); private: + void connectBinEditorWidget(QWidget *w); + bool doCreateBinEditor(quint64 startAddr, unsigned flags, + const QList<MemoryMarkup> &ml, const QPoint &pos, + QString title, QWidget *parent); + QList<QPointer<Core::IEditor> > m_editors; + QList<QPointer<MemoryView> > m_views; QPointer<DebuggerEngine> m_engine; }; diff --git a/src/plugins/debugger/memoryview.cpp b/src/plugins/debugger/memoryview.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8403815c1d73d0bc806f9f091b6d12f2ea8e1035 --- /dev/null +++ b/src/plugins/debugger/memoryview.cpp @@ -0,0 +1,196 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "memoryview.h" +#include "registerhandler.h" +#include "memoryagent.h" + +#include <bineditor/markup.h> + +#include <QtGui/QVBoxLayout> +#include <QtCore/QModelIndex> +#include <QtCore/QDebug> + +namespace Debugger { +namespace Internal { + +/*! + \class Debugger::Internal::MemoryView + \brief Base class for memory view tool windows + + Small tool-window that stays on top and displays a chunk of memory + based on the widget provided by the Bin editor plugin. + + Provides static functionality for handling a Bin Editor Widget + (soft dependency) via QMetaObject invocation. + + \sa Debugger::Internal::MemoryAgent, Debugger::DebuggerEngine +*/ + +MemoryView::MemoryView(QWidget *binEditor, QWidget *parent) : + QWidget(parent, Qt::Tool|Qt::WindowStaysOnTopHint), m_binEditor(binEditor) +{ + setAttribute(Qt::WA_DeleteOnClose); + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(binEditor); + setMinimumWidth(400); +} + +void MemoryView::setBinEditorRange(QWidget *w, quint64 address, int range, int blockSize) +{ + QMetaObject::invokeMethod(w, "setSizes", + Q_ARG(quint64, address), Q_ARG(int, range), Q_ARG(int, blockSize)); +} + +void MemoryView::setBinEditorReadOnly(QWidget *w, bool readOnly) +{ + w->setProperty("readOnly", QVariant(readOnly)); +} + +void MemoryView::setBinEditorNewWindowRequestAllowed(QWidget *w, bool a) +{ + w->setProperty("newWindowRequestAllowed", QVariant(a)); +} + +void MemoryView::setBinEditorMarkup(QWidget *w, const QList<MemoryMarkup> &ml) +{ + // Convert into bin editor markup and set. + QList<BINEditor::Markup> bml; + foreach (const MemoryMarkup &m, ml) + bml.push_back(BINEditor::Markup(m.address, m.length, m.color, m.toolTip)); + w->setProperty("markup", qVariantFromValue(bml)); +} + +void MemoryView::binEditorUpdateContents(QWidget *w) +{ + QMetaObject::invokeMethod(w, "updateContents"); +} + +void MemoryView::binEditorSetCursorPosition(QWidget *w, int p) +{ + QMetaObject::invokeMethod(w, "setCursorPosition", Q_ARG(int, p)); +} + +void MemoryView::binEditorAddData(QWidget *w, quint64 addr, const QByteArray &ba) +{ + QMetaObject::invokeMethod(w, "addData", + Q_ARG(quint64, addr / MemoryAgent::BinBlockSize), + Q_ARG(QByteArray, ba)); +} + +void MemoryView::setAddress(quint64 a) +{ + setBinEditorRange(m_binEditor, a, MemoryAgent::DataRange, MemoryAgent::BinBlockSize); +} + +void MemoryView::updateContents() +{ + binEditorUpdateContents(m_binEditor); +} + +void MemoryView::setMarkup(const QList<MemoryMarkup> &m) +{ + MemoryView::setBinEditorMarkup(m_binEditor, m); +} + +/*! + \class Debugger::Internal::RegisterMemoryView + \brief Memory view that shows the memory around the contents of a register + (such as stack pointer, program counter), + tracking changes of the register value. + + Connects to Debugger::Internal::RegisterHandler to listen for changes + of the register value. + + \note Some registers might switch to 0 (for example, 'rsi','rbp' + while stepping out of a function with parameters). + This must be handled gracefully. + + \sa Debugger::Internal::RegisterHandler, Debugger::Internal::RegisterWindow + \sa Debugger::Internal::MemoryAgent, Debugger::DebuggerEngine +*/ + +RegisterMemoryView::RegisterMemoryView(QWidget *binEditor, QWidget *parent) : + MemoryView(binEditor, parent), + m_registerIndex(0), m_registerAddress(0) +{ +} + +void RegisterMemoryView::slotRegisterSet(const QModelIndex &index) +{ + if (m_registerIndex != index.row()) + return; + const QVariant newAddressV = index.data(Qt::EditRole); + if (newAddressV.type() == QVariant::ULongLong) + setRegisterAddress(newAddressV.toULongLong()); +} + +QString RegisterMemoryView::title(const QString ®isterName, quint64 a) +{ + return tr("Memory at Register '%1' (0x%2)").arg(registerName).arg(a, 0, 16); +} + +void RegisterMemoryView::setRegisterAddress(quint64 v) +{ + if (v == m_registerAddress) { + updateContents(); + return; + } + m_registerAddress = v; + setAddress(v); + setWindowTitle(title(m_registerName, v)); + if (v) + setMarkup(registerMarkup(v, m_registerName)); +} + +QList<MemoryMarkup> RegisterMemoryView::registerMarkup(quint64 a, const QString &name) +{ + QList<MemoryMarkup> result; + result.push_back(MemoryMarkup(a, 1, QColor(Qt::blue).lighter(), + tr("Register '%1'").arg(name))); + return result; +} + +void RegisterMemoryView::init(RegisterHandler *h, int registerIndex) +{ + m_registerIndex = registerIndex; + m_registerName = QString::fromAscii(h->registerAt(registerIndex).name); + // Known issue: CDB might reset the model by changing the special + // registers it reports. + connect(h, SIGNAL(modelReset()), this, SLOT(close())); + connect(h, SIGNAL(registerSet(QModelIndex)), + this, SLOT(slotRegisterSet(QModelIndex))); + setRegisterAddress(h->registerAt(m_registerIndex).editValue().toULongLong()); +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/memoryview.h b/src/plugins/debugger/memoryview.h new file mode 100644 index 0000000000000000000000000000000000000000..1ba76c122d87be8933de931eca08d614e8d130c2 --- /dev/null +++ b/src/plugins/debugger/memoryview.h @@ -0,0 +1,95 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef DEBUGGER_INTERNAL_MEMORYVIEW_H +#define DEBUGGER_INTERNAL_MEMORYVIEW_H + +#include <QtGui/QWidget> + +QT_FORWARD_DECLARE_CLASS(QModelIndex) + +namespace Debugger { +namespace Internal { +class MemoryMarkup; +class RegisterHandler; + +class MemoryView : public QWidget +{ + Q_OBJECT +public: + explicit MemoryView(QWidget *binEditor, QWidget *parent = 0); + + static void setBinEditorRange(QWidget *w, quint64 address, int range, int blockSize); + static void setBinEditorReadOnly(QWidget *w, bool readOnly); + static void setBinEditorNewWindowRequestAllowed(QWidget *w, bool a); + static void setBinEditorMarkup(QWidget *w, const QList<MemoryMarkup> &ml); + static void binEditorSetCursorPosition(QWidget *w, int p); + static void binEditorUpdateContents(QWidget *w); + static void binEditorAddData(QWidget *w, quint64 addr, const QByteArray &a); + +public slots: + void updateContents(); + +protected: + void setAddress(quint64 a); + void setMarkup(const QList<MemoryMarkup> &m); + +private: + QWidget *m_binEditor; +}; + +class RegisterMemoryView : public MemoryView +{ + Q_OBJECT +public: + explicit RegisterMemoryView(QWidget *binEditor, QWidget *parent = 0); + + void init(RegisterHandler *rh, int index); + + static QList<MemoryMarkup> registerMarkup(quint64 a, const QString &name); + static QString title(const QString ®isterName, quint64 a = 0); + +private slots: + void slotRegisterSet(const QModelIndex &index); + +private: + void setRegisterAddress(quint64 v); + + int m_registerIndex; + QString m_registerName; + quint64 m_registerAddress; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_INTERNAL_MEMORYVIEW_H diff --git a/src/plugins/debugger/memoryviewwidget.cpp b/src/plugins/debugger/memoryviewwidget.cpp deleted file mode 100644 index 3b622d962df660da9fb5963b50794cd44da2280d..0000000000000000000000000000000000000000 --- a/src/plugins/debugger/memoryviewwidget.cpp +++ /dev/null @@ -1,666 +0,0 @@ -/************************************************************************** -** -** This file is part of Qt Creator -** -** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** No Commercial Usage -** -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -**************************************************************************/ - -#include "memoryviewwidget.h" -#include "memoryagent.h" -#include "registerhandler.h" - -#include <coreplugin/coreconstants.h> -#include <texteditor/fontsettings.h> - -#include <QtGui/QLabel> -#include <QtGui/QVBoxLayout> -#include <QtGui/QPlainTextEdit> -#include <QtGui/QScrollBar> -#include <QtGui/QToolButton> -#include <QtGui/QToolBar> -#include <QtGui/QTextCursor> -#include <QtGui/QTextBlock> -#include <QtGui/QTextDocument> -#include <QtGui/QIcon> -#include <QtGui/QFont> -#include <QtGui/QFontMetrics> -#include <QtGui/QMenu> - -#include <QtCore/QTextStream> -#include <QtCore/QDebug> -#include <QtCore/QVariant> - -#include <cctype> - -enum { debug = 0 }; - -// Formatting: 'aaaa:aaaa 0f ab... ASC..' -enum -{ - bytesPerLine = 16, - lineWidth = 11 + 4 * bytesPerLine -}; - -namespace Debugger { -namespace Internal { - -/*! - \class Debugger::Internal::MemoryViewWidget - \brief Base class for memory view tool windows - - Small tool-window that stays on top and displays a chunk of memory. - Provides next/previous browsing. - - Constructed by passing an instance to \c DebuggerEngine::addMemoryView() - which will pass it on to \c Debugger::Internal::MemoryAgent::addMemoryView() - to set up the signal connections to the engine. - - Provides API for marking text with a special format/color. - The formatting is stored as a list of struct MemoryViewWidget::Markup and applied - by converting into extra selections once data arrives in setData(). - - Provides a context menu that offers to open a subview from a pointer value - obtained from the memory shown (converted using the Abi). - - \sa Debugger::Internal::MemoryAgent, Debugger::DebuggerEngine - \sa ProjectExplorer::Abi -*/ - -const quint64 MemoryViewWidget::defaultLength = 128; - -MemoryViewWidget::MemoryViewWidget(QWidget *parent) : - QWidget(parent, Qt::Tool|Qt::WindowStaysOnTopHint), - m_previousButton(new QToolButton), - m_nextButton(new QToolButton), - m_textEdit(new QPlainTextEdit), - m_content(new QLabel), - m_address(0), - m_length(0), - m_requestedAddress(0), - m_requestedLength(0), - m_updateOnInferiorStop(false) -{ - setAttribute(Qt::WA_DeleteOnClose); - QVBoxLayout *layout = new QVBoxLayout(this); - - QToolBar *toolBar = new QToolBar; - toolBar->setObjectName(QLatin1String("MemoryViewWidgetToolBar")); - toolBar->setProperty("_q_custom_style_disabled", QVariant(true)); - toolBar->setIconSize(QSize(16, 16)); - m_previousButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_PREV))); - connect(m_previousButton, SIGNAL(clicked()), this, SLOT(slotPrevious())); - toolBar->addWidget(m_previousButton); - m_nextButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_NEXT))); - connect(m_nextButton, SIGNAL(clicked()), this, SLOT(slotNext())); - toolBar->addWidget(m_nextButton); - - layout->addWidget(toolBar); - m_textEdit->setObjectName(QLatin1String("MemoryViewWidgetTextEdit")); - m_textEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - m_textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); - m_textEdit->setReadOnly(true); - m_textEdit->setWordWrapMode(QTextOption::NoWrap); - m_textEdit->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_textEdit, SIGNAL(customContextMenuRequested(QPoint)), - this, SLOT(slotContextMenuRequested(QPoint))); - // Text: Pick a fixed font and set minimum size to accommodate default length with vertical scrolling - const QFont fixedFont(TextEditor::FontSettings::defaultFixedFontFamily(), TextEditor::FontSettings::defaultFontSize()); - const QFontMetrics metrics(fixedFont); - const QSize lineSize = metrics.size(Qt::TextSingleLine , QString(lineWidth, QLatin1Char('0'))); - int defaultLineCount = defaultLength / bytesPerLine; - if (defaultLength % bytesPerLine) - defaultLineCount++; - const QSize textSize(lineSize.width() + m_textEdit->verticalScrollBar()->width() + 10, - lineSize.height() * defaultLineCount + 10); - m_textEdit->setFont(fixedFont); - m_textEdit->setMinimumSize(textSize); - m_textEdit->installEventFilter(this); - layout->addWidget(m_textEdit); -} - -void MemoryViewWidget::engineStateChanged(Debugger::DebuggerState s) -{ - switch (s) { - case Debugger::InferiorUnrunnable: - setBrowsingEnabled(true); - break; - case Debugger::InferiorStopOk: - setBrowsingEnabled(true); - if (m_updateOnInferiorStop) - requestMemory(); - break; - case Debugger::DebuggerFinished: - close(); - break; - default: - setBrowsingEnabled(false); - break; - } -} - -void MemoryViewWidget::setBrowsingEnabled(bool b) -{ - m_previousButton->setEnabled(b && m_address >= m_length); - m_nextButton->setEnabled(b); -} - -void MemoryViewWidget::clear() -{ - m_data.clear(); - m_textEdit->setExtraSelections(QList<QTextEdit::ExtraSelection>()); - m_textEdit->setPlainText(tr("No data available.")); - setBrowsingEnabled(false); - updateTitle(); -} - -void MemoryViewWidget::requestMemory() -{ - requestMemory(m_address, m_length); -} - -void MemoryViewWidget::requestMemory(quint64 address, quint64 length) -{ - m_requestedAddress = address; - m_requestedLength = length; - - // For RegisterMemoryViewWidget, the register values sometimes switch to 0 - // while stepping, handle gracefully. - if (!address || !length) { - m_address = address; - m_length = length; - clear(); - return; - } - - // Is this the first request and no data available yet? -> Set initial state. - if (m_data.isEmpty() && !m_address && !m_length) { - m_address = address; - m_length = length; - updateTitle(); - m_textEdit->setPlainText(tr("Fetching %1 bytes...").arg(length)); - setBrowsingEnabled(false); - } - if (debug) - qDebug() << this << "requestMemory()" << m_requestedAddress << m_requestedLength - << " currently at: " << m_address << m_length; - emit memoryRequested(m_requestedAddress, m_requestedLength); -} - -void MemoryViewWidget::setTitle(const QString &t) -{ - setWindowTitle(t); -} - -void MemoryViewWidget::slotNext() -{ - requestMemory(m_address + m_length, m_length); -} - -void MemoryViewWidget::slotPrevious() -{ - if (m_address >= m_length) - requestMemory(m_address - m_length, m_length); -} - -// Convert address to line and column in range 0..(n - 1), return false -// if out of range. -bool MemoryViewWidget::addressToLineColumn(quint64 posAddress, - int *lineIn /* = 0 */, int *columnIn /* = 0 */, - quint64 *lineStartIn /* = 0 */) const -{ - if (posAddress < m_address) - return false; - const quint64 offset = posAddress - m_address; - if (offset >= quint64(m_data.size())) - return false; - const quint64 line = offset / bytesPerLine; - const quint64 lineStart = m_address + line * bytesPerLine; - if (lineStartIn) - *lineStartIn = lineStart; - if (lineIn) - *lineIn = int(line); - const int column = 3 * int(offset % bytesPerLine) + 10; - if (columnIn) - *columnIn = column; - if (debug) - qDebug() << this << "at" << m_address << " addressToLineColumn " - << posAddress << "->" << line << ',' << column << " lineAt" << lineStart; - return true; -} - -// Return address at position -quint64 MemoryViewWidget::addressAt(const QPoint &textPos) const -{ - QTextCursor cursor = m_textEdit->cursorForPosition(textPos); - if (cursor.isNull()) - return 0; - const int line = cursor.blockNumber(); - const int column = cursor.columnNumber() - 1; - const quint64 lineAddress = m_address + line * bytesPerLine; - const int byte = (qMax(0, column - 9)) / 3; - if (byte >= bytesPerLine) // Within ASC part - return 0; - return lineAddress + byte; -} - -void MemoryViewWidget::slotContextMenuRequested(const QPoint &pos) -{ - QMenu *menu = m_textEdit->createStandardContextMenu(); - menu->addSeparator(); - // Add action offering to open a sub-view with a pointer read from the memory - // at the location: Dereference the chunk of memory as pointer address. - QAction *derefPointerAction = 0; - quint64 pointerValue = 0; - if (!m_data.isEmpty()) { - const quint64 pointerSize = m_abi.wordWidth() / 8; - quint64 contextAddress = addressAt(pos); - if (const quint64 remainder = contextAddress % pointerSize) // Pad pointer location. - contextAddress -= remainder; - // Dereference pointer from location - if (contextAddress) { - const quint64 dataOffset = contextAddress - address(); - if (pointerSize && (dataOffset + pointerSize) <= quint64(m_data.size())) { - const unsigned char *data = reinterpret_cast<const unsigned char *>(m_data.constData() + dataOffset); - pointerValue = MemoryAgent::readInferiorPointerValue(data, m_abi); - } - } - } // has data - if (pointerValue) { - const QString msg = tr("Open Memory View at Pointer Value 0x%1") - .arg(pointerValue, 0, 16); - derefPointerAction = menu->addAction(msg); - } else { - derefPointerAction = menu->addAction("Open Memory View at Pointer Value"); - derefPointerAction->setEnabled(false); - } - const QPoint globalPos = m_textEdit->mapToGlobal(pos); - QAction *action = menu->exec(globalPos); - if (!action) - return; - if (action == derefPointerAction) { - emit openViewRequested(pointerValue, MemoryViewWidget::defaultLength, globalPos); - return; - } -} - -// Format address as in binary editor '0000:00AB' onto a stream set up for hex output. -static inline void formatAddressToHexStream(QTextStream &hexStream, quint64 address) -{ - hexStream.setFieldWidth(4); - hexStream << ((address >> 16) & 0xFFFF); - hexStream.setFieldWidth(1); - hexStream << ':'; - hexStream.setFieldWidth(4); - hexStream << (address & 0xFFFF); -} - -// Return formatted address for window title: Prefix + binary editor format: '0x0000:00AB' -static inline QString formattedAddress(quint64 a) -{ - QString rc = QLatin1String("0x"); - QTextStream str(&rc); - str.setIntegerBase(16); - str.setPadChar(QLatin1Char('0')); - formatAddressToHexStream(str, a); - return rc; -} - -// Format data as in binary editor '0000:00AB 0A A3 ..ccc' -QString MemoryViewWidget::formatData(quint64 startAddress, const QByteArray &data) -{ - QString rc; - rc.reserve(5 * data.size()); - const quint64 endAddress = startAddress + data.size(); - QTextStream str(&rc); - str.setIntegerBase(16); - str.setPadChar(QLatin1Char('0')); - str.setFieldAlignment(QTextStream::AlignRight); - for (quint64 address = startAddress; address < endAddress; address += 16) { - formatAddressToHexStream(str, address); - // Format hex bytes - const int dataStart = int(address - startAddress); - const int dataEnd = qMin(dataStart + int(bytesPerLine), data.size()); - for (int i = dataStart; i < dataEnd; i++) { - str.setFieldWidth(1); - str << ' '; - str.setFieldWidth(2); - const char c = data.at(i); - unsigned char uc = c; - str << unsigned(uc); - } - // Pad for character part - str.setFieldWidth(1); - if (const int remainder = int(bytesPerLine) - (dataEnd - dataStart)) - str << QString(3 * remainder, QLatin1Char(' ')); - // Format characters - str << ' '; - for (int i = dataStart; i < dataEnd; i++) { - const char c = data.at(i); - str << (c >= 0 && std::isprint(c) ? c : '.'); // MSVC has an assert on c>=0. - } - str << '\n'; - } - return rc; -} - -void MemoryViewWidget::updateTitle() -{ - const QString title = tr("Memory at %1").arg(formattedAddress(address())); - setTitle(title); -} - -// Convert an markup range into a list of selections for the bytes, -// resulting in a rectangular selection in the bytes area (provided range -// is within data available). -bool MemoryViewWidget::markUpToSelections(const Markup &r, - QList<QTextEdit::ExtraSelection> *extraSelections) const -{ - // Fully covered? - if (r.address < m_address) - return false; - const quint64 rangeEnd = r.address + r.size; - if (rangeEnd > (m_address + quint64(m_data.size()))) - return false; - - QTextCursor cursor = m_textEdit->textCursor(); - cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor); - - // Goto first position - int line; - int column; - quint64 lineStartAddress; - - if (!addressToLineColumn(r.address, &line, &column, &lineStartAddress)) - return false; - - if (line) - cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, line); - if (column) - cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, column - 1); - - quint64 current = r.address; - // Mark rectangular area in the bytes section - while (true) { - // Mark in current line - quint64 nextLineAddress = lineStartAddress + bytesPerLine; - const int numberOfCharsToMark = 3 * int(qMin(nextLineAddress, rangeEnd) - current); - cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, numberOfCharsToMark); - QTextEdit::ExtraSelection sel; - sel.cursor = cursor; - sel.format = r.format; - extraSelections->push_back(sel); - if (nextLineAddress >= rangeEnd) - break; - // Goto beginning of next line, past address. - cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor); - cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor); - cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 9); - lineStartAddress += bytesPerLine; - current = lineStartAddress; - } - return true; -} - -void MemoryViewWidget::clearMarkup() -{ - m_markup.clear(); - m_textEdit->setExtraSelections(QList<QTextEdit::ExtraSelection>()); -} - -void MemoryViewWidget::addMarkup(quint64 begin, quint64 size, - const QTextCharFormat &fmt, const QString &toolTip) -{ - m_markup.push_back(Markup(begin, size, fmt, toolTip)); -} - -void MemoryViewWidget::addMarkup(quint64 begin, quint64 size, - const QColor &background, const QString &toolTip) -{ - QTextCharFormat format = textCharFormat(); - format.setBackground(QBrush(background)); - addMarkup(begin, size, format, toolTip); -} - -QTextCharFormat MemoryViewWidget::textCharFormat() const -{ - return m_textEdit->currentCharFormat(); -} - -void MemoryViewWidget::setData(const QByteArray &a) -{ - if (debug) - qDebug() << this << m_requestedAddress << m_requestedLength << "received" << a.size(); - - if (quint64(a.size()) < m_requestedLength) { - const QString msg = QString::fromLatin1("Warning: %1 received only %2 bytes of %3 at 0x%4") - .arg(QString::fromAscii(metaObject()->className())) - .arg(a.size()).arg(m_requestedLength).arg(m_requestedAddress, 0, 16); - qWarning("%s", qPrintable(msg)); - } - - if (m_address != m_requestedAddress || m_length != m_requestedLength) { - m_address = m_requestedAddress; - m_length = m_requestedLength; - updateTitle(); - } - - if (a.isEmpty()) { - clear(); - return; - } - - m_data = a; - - QList<QTextEdit::ExtraSelection> extra; - m_textEdit->setExtraSelections(extra); - m_textEdit->setPlainText(MemoryViewWidget::formatData(address(), a)); - // Do markup which is in visible range now. - foreach (const Markup &r, m_markup) - markUpToSelections(r, &extra); - if (!extra.isEmpty()) - m_textEdit->setExtraSelections(extra); - setBrowsingEnabled(true); -} - -// Find markup by address. -int MemoryViewWidget::indexOfMarkup(quint64 address) const -{ - const int size = m_markup.size(); - for (int m = 0; m < size; m++) - if (m_markup.at(m).covers(address)) - return m; - return -1; -} - -bool MemoryViewWidget::eventFilter(QObject *o, QEvent *e) -{ - if (o != m_textEdit || e->type() != QEvent::ToolTip) - return QWidget::eventFilter(o, e); - // ToolTip handling: is the cursor over an address range that has a tooltip - // defined in the markup list? - const QHelpEvent *he = static_cast<const QHelpEvent *>(e); - if (const quint64 toolTipAddress = addressAt(he->pos())) { - const int mIndex = indexOfMarkup(toolTipAddress); - if (mIndex != -1) { - m_textEdit->setToolTip(m_markup.at(mIndex).toolTip); - } else { - m_textEdit->setToolTip(QString()); - } - } - return QWidget::eventFilter(o, e); -} - -/*! - \class Debugger::Internal::LocalsMemoryViewWidget - \brief Memory view that shows the memory at the location of a local variable. - - Refreshes whenever Debugger::InferiorStopOk is reported. - - \sa Debugger::Internal::WatchWindow - \sa Debugger::Internal::MemoryAgent, Debugger::DebuggerEngine -*/ - -LocalsMemoryViewWidget::LocalsMemoryViewWidget(QWidget *parent) : - MemoryViewWidget(parent), m_variableAddress(0) -{ - setUpdateOnInferiorStop(true); -} - -void LocalsMemoryViewWidget::init(quint64 variableAddress, quint64 size, const QString &name) -{ - m_variableAddress = variableAddress; - m_variableSize = size; - m_variableName = name; - // Size may be 0. - addMarkup(variableAddress, qMax(size, quint64(1)), Qt::lightGray); - requestMemory(m_variableAddress, qMax(size, quint64(defaultLength))); - if (debug) - qDebug() << this << "init" << variableAddress << m_variableName << m_variableSize; -} - -void LocalsMemoryViewWidget::updateTitle() -{ - const QString variableAddress = formattedAddress(m_variableAddress); - if (address() == m_variableAddress) { - const QString title = tr("Memory at '%1' (%2)") - .arg(m_variableName, variableAddress); - setTitle(title); - } else if (address() > m_variableAddress) { - const QString title = tr("Memory at '%1' (%2 + %3)") - .arg(m_variableName, variableAddress) - .arg(address() - m_variableAddress); - setTitle(title); - } else if (address() < m_variableAddress) { - const QString title = tr("Memory at '%1' (%2 - %3)") - .arg(m_variableName, variableAddress) - .arg(m_variableAddress - address()); - setTitle(title); - } -} - -/*! - \class Debugger::Internal::RegisterMemoryViewWidget - \brief Memory view that shows the memory around the contents of a register - (such as stack pointer, program counter), - tracking changes of the register value. - - Connects to Debugger::Internal::RegisterHandler to listen for changes - of the register value. - - \sa Debugger::Internal::RegisterHandler, Debugger::Internal::RegisterWindow - \sa Debugger::Internal::MemoryAgent, Debugger::DebuggerEngine -*/ - -RegisterMemoryViewWidget::Markup::Markup(quint64 a, quint64 s, - const QTextCharFormat &fmt, const QString &tt) : - address(a), size(s), format(fmt), toolTip(tt) -{ -} - -RegisterMemoryViewWidget::RegisterMemoryViewWidget(QWidget *parent) : - MemoryViewWidget(parent), - m_registerIndex(-1), - m_registerAddress(0), - m_offset(0) -{ - setUpdateOnInferiorStop(false); // We update on register changed. -} - -void RegisterMemoryViewWidget::updateTitle() -{ - const quint64 shownAddress = address() + m_offset; - const QString registerAddress = formattedAddress(m_registerAddress); - if (shownAddress == m_registerAddress) { - const QString title = tr("Memory at Register '%1' (%2)") - .arg(m_registerName, registerAddress); - setTitle(title); - } else if (shownAddress > m_registerAddress) { - const QString title = tr("Memory at Register '%1' (%2 + %3)") - .arg(m_registerName, registerAddress) - .arg(shownAddress - m_registerAddress); - setTitle(title); - } else if (shownAddress < m_registerAddress) { - const QString title = tr("Memory at Register '%1' (%2 - %3)") - .arg(m_registerName, registerAddress) - .arg(m_registerAddress - shownAddress); - setTitle(title); - } -} - -void RegisterMemoryViewWidget::setRegisterAddress(quint64 a) -{ - if (!a) { // Registers might switch to 0 (for example, 'rsi' while stepping out). - m_offset = m_registerAddress = a; - requestMemory(0, 0); - return; - } - if (m_registerAddress == a) { // Same value: just re-fetch - requestMemory(); - return; - } - // Show an area around that register - m_registerAddress = a; - const quint64 range = MemoryViewWidget::defaultLength / 2; - const quint64 end = a + range; - const quint64 begin = a >= range ? a - range : 0; - m_offset = m_registerAddress - begin; - // Mark one byte showing the register - clearMarkup(); - addMarkup(m_registerAddress, 1, Qt::lightGray, tr("Register %1").arg(m_registerName)); - requestMemory(begin, end - begin); -} - -void RegisterMemoryViewWidget::slotRegisterSet(const QModelIndex &index) -{ - if (m_registerIndex != index.row()) - return; - const QVariant newAddressV = index.data(Qt::EditRole); - if (newAddressV.type() == QVariant::ULongLong) { - if (debug) - qDebug() << this << m_registerIndex << m_registerName << "slotRegisterSet" << newAddressV; - setRegisterAddress(newAddressV.toULongLong()); - } -} - -void RegisterMemoryViewWidget::init(int registerIndex, RegisterHandler *h) -{ - m_registerIndex = registerIndex; - m_registerName = QString::fromAscii(h->registerAt(registerIndex).name); - if (debug) - qDebug() << this << "init" << registerIndex << m_registerName; - // Known issue: CDB might reset the model by changing the special - // registers it reports. - connect(h, SIGNAL(modelReset()), this, SLOT(close())); - connect(h, SIGNAL(registerSet(QModelIndex)), - this, SLOT(slotRegisterSet(QModelIndex))); - setRegisterAddress(h->registerAt(m_registerIndex).editValue().toULongLong()); -} - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/memoryviewwidget.h b/src/plugins/debugger/memoryviewwidget.h deleted file mode 100644 index 505bad2bdd30ec577e0d3bed5c09d894f7111e56..0000000000000000000000000000000000000000 --- a/src/plugins/debugger/memoryviewwidget.h +++ /dev/null @@ -1,187 +0,0 @@ -/************************************************************************** -** -** This file is part of Qt Creator -** -** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** No Commercial Usage -** -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -**************************************************************************/ - -#ifndef MEMORYTOOLTIP_H -#define MEMORYTOOLTIP_H - -#include "debuggerconstants.h" - -#include <projectexplorer/abi.h> - -#include <QtGui/QTextEdit> // QTextEdit::ExtraSelection -#include <QtCore/QList> - -QT_BEGIN_NAMESPACE -class QLabel; -class QModelIndex; -class QPlainTextEdit; -class QToolButton; -class QTextCharFormat; -QT_END_NAMESPACE - -namespace Debugger { -class DebuggerEngine; -namespace Internal { -class RegisterHandler; - -// Documentation inside. -class MemoryViewWidget : public QWidget -{ - Q_OBJECT -public: - // Address range to be marked with special format - struct Markup - { - Markup(quint64 a = 0, quint64 s = 0, - const QTextCharFormat &fmt = QTextCharFormat(), - const QString &toolTip = QString()); - bool covers(quint64 a) const { return a >= address && a < (address + size); } - - quint64 address; - quint64 size; - QTextCharFormat format; - QString toolTip; - }; - - explicit MemoryViewWidget(QWidget *parent = 0); - - quint64 address() const { return m_address; } - quint64 length() const { return m_length; } - - // How read an address used for 'dereference pointer at' context menu action - void setAbi(const ProjectExplorer::Abi &a) { m_abi = a; } - ProjectExplorer::Abi abi() const { return m_abi; } - - bool updateOnInferiorStop() const { return m_updateOnInferiorStop; } - void setUpdateOnInferiorStop(bool v) { m_updateOnInferiorStop = v ; } - - QTextCharFormat textCharFormat() const; - - QList<Markup> markup() const { return m_markup; } - void setMarkup(const QList<Markup> &m) { clearMarkup(); m_markup = m; } - - static QString formatData(quint64 address, const QByteArray &d); - - static const quint64 defaultLength; - - virtual bool eventFilter(QObject *, QEvent *); - -signals: - // Fetch memory and use setData(). - void memoryRequested(quint64 address, quint64 length); - // Open a (sub) view from context menu - void openViewRequested(quint64 address, quint64 length, const QPoint &pos); - -public slots: - void setData(const QByteArray &a); // Set to empty to indicate non-available data - void engineStateChanged(Debugger::DebuggerState s); - void addMarkup(quint64 begin, quint64 size, const QTextCharFormat &, - const QString &toolTip = QString()); - void addMarkup(quint64 begin, quint64 size, const QColor &background, - const QString &toolTip = QString()); - void clear(); - void clearMarkup(); - void requestMemory(); - void requestMemory(quint64 address, quint64 length); - -protected: - virtual void updateTitle(); - void setTitle(const QString &); - -private slots: - void slotNext(); - void slotPrevious(); - void slotContextMenuRequested(const QPoint &pos); - -private: - void setBrowsingEnabled(bool); - quint64 addressAt(const QPoint &textPos) const; - bool addressToLineColumn(quint64 address, int *line = 0, int *column = 0, - quint64 *lineStart = 0) const; - bool markUpToSelections(const Markup &r, - QList<QTextEdit::ExtraSelection> *extraSelections) const; - int indexOfMarkup(quint64 address) const; - - QToolButton *m_previousButton; - QToolButton *m_nextButton; - QPlainTextEdit *m_textEdit; - QLabel *m_content; - quint64 m_address; - quint64 m_length; - quint64 m_requestedAddress; - quint64 m_requestedLength; - ProjectExplorer::Abi m_abi; - QByteArray m_data; - bool m_updateOnInferiorStop; - QList<Markup> m_markup; -}; - -class LocalsMemoryViewWidget : public MemoryViewWidget -{ - Q_OBJECT -public: - explicit LocalsMemoryViewWidget(QWidget *parent = 0); - void init(quint64 variableAddress, quint64 size, const QString &name); - -private: - virtual void updateTitle(); - - quint64 m_variableAddress; - quint64 m_variableSize; - QString m_variableName; -}; - -class RegisterMemoryViewWidget : public MemoryViewWidget -{ - Q_OBJECT -public: - explicit RegisterMemoryViewWidget(QWidget *parent = 0); - void init(int registerIndex, RegisterHandler *h); - -private slots: - void slotRegisterSet(const QModelIndex &); - -private: - virtual void updateTitle(); - void setRegisterAddress(quint64 a); - - int m_registerIndex; - quint64 m_registerAddress; - quint64 m_offset; - QString m_registerName; -}; - -} // namespace Internal -} // namespace Debugger - -#endif // MEMORYTOOLTIP_H diff --git a/src/plugins/debugger/registerwindow.cpp b/src/plugins/debugger/registerwindow.cpp index 664c527de806408f0908de2e7777b3abc344e3c2..62ba31da0f09fad47d9d90fef2526b530f230a5c 100644 --- a/src/plugins/debugger/registerwindow.cpp +++ b/src/plugins/debugger/registerwindow.cpp @@ -31,13 +31,14 @@ **************************************************************************/ #include "registerwindow.h" -#include "memoryviewwidget.h" +#include "memoryview.h" #include "debuggeractions.h" #include "debuggerconstants.h" #include "debuggercore.h" #include "debuggerengine.h" #include "registerhandler.h" #include "watchdelegatewidgets.h" +#include "memoryagent.h" #include <utils/qtcassert.h> #include <utils/savedaction.h> @@ -210,6 +211,7 @@ void RegisterWindow::contextMenuEvent(QContextMenuEvent *ev) actEditMemory->setText(tr("Open Memory Editor")); actViewMemory->setText(tr("Open Memory View at Value of Register")); actEditMemory->setEnabled(false); + actViewMemory->setEnabled(false); } menu.addSeparator(); @@ -241,13 +243,13 @@ void RegisterWindow::contextMenuEvent(QContextMenuEvent *ev) resizeColumnsToContents(); else if (act == actReload) engine->reloadRegisters(); - else if (act == actEditMemory) - engine->openMemoryView(address); - else if (act == actViewMemory) { - RegisterMemoryViewWidget *w = new RegisterMemoryViewWidget(this); - w->move(position); - w->init(idx.row(), handler); - engine->addMemoryView(w); + else if (act == actEditMemory) { + const QString registerName = QString::fromAscii(aRegister.name, address); + engine->openMemoryView(address, 0, RegisterMemoryView::registerMarkup(address, registerName), + QPoint(), RegisterMemoryView::title(registerName), 0); + } else if (act == actViewMemory) { + engine->openMemoryView(idx.row(), DebuggerEngine::MemoryTrackRegister|DebuggerEngine::MemoryView, + QList<MemoryMarkup>(), position, QString(), this); } else if (act == act16) handler->setNumberBase(16); else if (act == act10) diff --git a/src/plugins/debugger/stackwindow.cpp b/src/plugins/debugger/stackwindow.cpp index b16c2f74ad4ab001cf5dba01b6131798128c1f99..02d777db6d6c60aad370c772fc8c561df15d9c87 100644 --- a/src/plugins/debugger/stackwindow.cpp +++ b/src/plugins/debugger/stackwindow.cpp @@ -37,6 +37,7 @@ #include "debuggerconstants.h" #include "debuggercore.h" #include "debuggerengine.h" +#include "memoryagent.h" #include <utils/qtcassert.h> #include <utils/savedaction.h> @@ -169,9 +170,14 @@ void StackWindow::contextMenuEvent(QContextMenuEvent *ev) copyContentsToClipboard(); else if (act == actAdjust) resizeColumnsToContents(); - else if (act == actShowMemory) - engine->openMemoryView(address); - else if (act == actShowDisassembler) + else if (act == actShowMemory) { + const QString title = tr("Memory at Frame #%1 (%2) 0x%3)"). + arg(row).arg(frame.function).arg(address, 0, 16); + QList<MemoryMarkup> ml; + ml.push_back(MemoryMarkup(address, 1, QColor(Qt::blue).lighter(), + tr("Frame #%1 (%2)").arg(row).arg(frame.function))); + engine->openMemoryView(address, 0, ml, QPoint(), title); + } else if (act == actShowDisassembler) engine->openDisassemblerView(frame); else if (act == actLoadSymbols) engine->loadSymbolsForStack(); diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp index c874238c8a100624f068f685042a15ecb1a5cd0f..ba37c5075d66065cdc33916bb1e3f75fea1f1bbd 100644 --- a/src/plugins/debugger/watchwindow.cpp +++ b/src/plugins/debugger/watchwindow.cpp @@ -42,8 +42,7 @@ #include "watchdelegatewidgets.h" #include "watchhandler.h" #include "debuggertooltipmanager.h" -#include "memoryviewwidget.h" - +#include "memoryagent.h" #include <utils/qtcassert.h> #include <utils/savedaction.h> @@ -226,12 +225,11 @@ static int memberVariableRecursion(const QModelIndex &m, \sa Debugger::Internal::MemoryViewWidget */ -typedef QList<MemoryViewWidget::Markup> MemoryViewWidgetMarkup; +typedef QList<MemoryMarkup> MemoryMarkupList; -static inline MemoryViewWidgetMarkup +static inline MemoryMarkupList variableMemoryMarkup(const QModelIndex &m, quint64 address, quint64 size, bool sizeIsEstimate, - const QTextCharFormat &defaultFormat, const QColor &defaultBackground) { enum { debug = 0 }; @@ -239,7 +237,7 @@ static inline MemoryViewWidgetMarkup // Starting out from base, create an array representing the area filled with base // color. Fill children with some unique color numbers, // leaving the padding areas of the parent colored with the base color. - MemoryViewWidgetMarkup result; + MemoryMarkupList result; const QString name = nameOf(m); int colorNumber = 0; ColorNumberToolTipVector ranges(size, ColorNumberToolTipPair(colorNumber, name)); @@ -269,21 +267,19 @@ static inline MemoryViewWidgetMarkup const ColorNumberToolTipPair &range = ranges.at(i); if (result.isEmpty() || lastColorNumber != range.first) { lastColorNumber = range.first; - QTextCharFormat format = defaultFormat; - if (range.first == 0) { // Base color: Parent - format.setBackground(QBrush(baseColor)); - } else { + QColor color = baseColor; // Base color: Parent + if (range.first) { if (childNumber++ & 1) { // Alternating member colors. - format.setBackground(QBrush(memberColor1)); + color = memberColor1; memberColor1 = memberColor1.darker(120); } else { - format.setBackground(QBrush(memberColor2)); + color = memberColor2; memberColor2 = memberColor2.darker(120); } } // color switch - result.push_back(MemoryViewWidget::Markup(address + i, 1, format, range.second)); + result.push_back(MemoryMarkup(address + i, 1, color, range.second)); } else { - result.back().size++; + result.back().length++; } } @@ -297,8 +293,8 @@ static inline MemoryViewWidgetMarkup name = ranges.at(i).second; } dbg << '\n'; - foreach (const MemoryViewWidget::Markup &m, result) - dbg << m.address << ' ' << m.size << ' ' << m.toolTip << '\n'; + foreach (const MemoryMarkup &m, result) + dbg << m.address << ' ' << m.length << ' ' << m.toolTip << '\n'; } return result; @@ -306,11 +302,12 @@ static inline MemoryViewWidgetMarkup // Convenience to create a memory view of a variable. static void addVariableMemoryView(DebuggerEngine *engine, + bool separateView, const QModelIndex &m, bool deferencePointer, - const QPoint &p, QWidget *parent) + const QPoint &p, + QWidget *parent) { const QColor background = parent->palette().color(QPalette::Normal, QPalette::Base); - LocalsMemoryViewWidget *w = new LocalsMemoryViewWidget(parent); const quint64 address = deferencePointer ? pointerValueOf(m) : addressOf(m); // Fixme: Get the size of pointee (see variableMemoryMarkup())? // Also, gdb does not report the size yet as of 8.4.2011 @@ -319,13 +316,11 @@ static void addVariableMemoryView(DebuggerEngine *engine, const quint64 size = sizeIsEstimate ? 1024 : typeSize; if (!address) return; - const MemoryViewWidgetMarkup markup - = variableMemoryMarkup(m, address, size, sizeIsEstimate, - w->textCharFormat(), background); - w->init(address, qMax(size, LocalsMemoryViewWidget::defaultLength), nameOf(m)); - w->setMarkup(markup); - w->move(p); - engine->addMemoryView(w); + const QList<MemoryMarkup> markup = + variableMemoryMarkup(m, address, size, sizeIsEstimate, background); + const unsigned flags = separateView ? (DebuggerEngine::MemoryView|DebuggerEngine::MemoryReadOnly) : 0; + const QString title = WatchWindow::tr("Memory at Variable '%1' (0x%2)").arg(nameOf(m)).arg(address, 0, 16); + engine->openMemoryView(address, flags, markup, p, title, parent); } ///////////////////////////////////////////////////////////////////// @@ -724,17 +719,17 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev) watchExpression(newExp); } } else if (act == actOpenMemoryEditAtVariableAddress) { - currentEngine()->openMemoryView(address); + addVariableMemoryView(currentEngine(), false, mi0, false, ev->globalPos(), this); } else if (act == actOpenMemoryEditAtPointerValue) { - currentEngine()->openMemoryView(pointerValue); + addVariableMemoryView(currentEngine(), false, mi0, true, ev->globalPos(), this); } else if (act == actOpenMemoryEditor) { AddressDialog dialog; if (dialog.exec() == QDialog::Accepted) - currentEngine()->openMemoryView(dialog.address()); + currentEngine()->openMemoryView(dialog.address(), false, MemoryMarkupList(), QPoint()); } else if (act == actOpenMemoryViewAtVariableAddress) { - addVariableMemoryView(currentEngine(), mi0, false, ev->globalPos(), this); + addVariableMemoryView(currentEngine(), true, mi0, false, ev->globalPos(), this); } else if (act == actOpenMemoryViewAtPointerValue) { - addVariableMemoryView(currentEngine(), mi0, true, ev->globalPos(), this); + addVariableMemoryView(currentEngine(), true, mi0, true, ev->globalPos(), this); } else if (act == actSetWatchpointAtVariableAddress) { setWatchpoint(address, size); } else if (act == actSetWatchpointAtPointerValue) {