diff options
author | hjk <hjk@theqtcompany.com> | 2016-07-14 10:00:15 +0200 |
---|---|---|
committer | hjk <hjk@qt.io> | 2016-07-21 06:20:52 +0000 |
commit | 6e925910b460a8accec07de8d37f4610678a41b5 (patch) | |
tree | c71aa3d260a8d259d588814232d23ab324079ade /src/plugins | |
parent | 66142d94d2a7dad970c4dcc37e5bd37f62e4d933 (diff) | |
download | qt-creator-6e925910b460a8accec07de8d37f4610678a41b5.tar.gz |
Debugger/BinEditor: Standardize interface
Change-Id: I4acf6bc7648e57c564e86023176ae3905a293a99
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
Diffstat (limited to 'src/plugins')
26 files changed, 583 insertions, 644 deletions
diff --git a/src/plugins/bineditor/bineditor.pro b/src/plugins/bineditor/bineditor.pro index 3dde3eaf67..1603f77e08 100644 --- a/src/plugins/bineditor/bineditor.pro +++ b/src/plugins/bineditor/bineditor.pro @@ -3,6 +3,7 @@ include(../../qtcreatorplugin.pri) DEFINES += BINEDITOR_LIBRARY HEADERS += bineditorplugin.h \ + bineditorservice.h \ bineditorwidget.h \ bineditorconstants.h \ bineditor_global.h \ diff --git a/src/plugins/bineditor/bineditor.qbs b/src/plugins/bineditor/bineditor.qbs index 8d2138598d..6a94295cef 100644 --- a/src/plugins/bineditor/bineditor.qbs +++ b/src/plugins/bineditor/bineditor.qbs @@ -11,12 +11,11 @@ QtcPlugin { Depends { name: "TextEditor" } files: [ - "bineditorwidget.cpp", - "bineditorwidget.h", - "bineditorconstants.h", "bineditor_global.h", - "bineditorplugin.cpp", - "bineditorplugin.h", + "bineditorconstants.h", + "bineditorwidget.cpp", "bineditorwidget.h", + "bineditorplugin.cpp", "bineditorplugin.h", + "bineditorservice.h", "markup.cpp", "markup.h", ] diff --git a/src/plugins/bineditor/bineditorplugin.cpp b/src/plugins/bineditor/bineditorplugin.cpp index 6f5f75dfc2..c1f9906a58 100644 --- a/src/plugins/bineditor/bineditorplugin.cpp +++ b/src/plugins/bineditor/bineditorplugin.cpp @@ -26,6 +26,7 @@ #include "bineditorplugin.h" #include "bineditorwidget.h" #include "bineditorconstants.h" +#include "bineditorservice.h" #include <coreplugin/icore.h> @@ -59,30 +60,6 @@ using namespace Utils; using namespace Core; namespace BinEditor { - -///////////////////////////////// BinEditorWidgetFactory ////////////////////////////////// - -/*! - \class BinEditor::BinEditorWidgetFactory - \brief The BinEditorWidgetFactory class offers a service registered with - PluginManager to create bin editor widgets for plugins - without direct linkage. - - \sa ExtensionSystem::PluginManager::getObjectByClassName, ExtensionSystem::invoke -*/ - -class BinEditorWidgetFactory : public QObject -{ - Q_OBJECT -public: - BinEditorWidgetFactory() {} - - Q_INVOKABLE QWidget *createWidget(QWidget *parent) - { - return new BinEditorWidget(parent); - } -}; - namespace Internal { class BinEditorFind : public IFindSupport @@ -230,12 +207,10 @@ public: setId(Core::Constants::K_DEFAULT_BINARY_EDITOR_ID); setMimeType(QLatin1String(BinEditor::Constants::C_BINEDITOR_MIMETYPE)); m_widget = parent; - connect(m_widget, &BinEditorWidget::dataRequested, - this, &BinEditorDocument::provideData); - connect(m_widget, &BinEditorWidget::newRangeRequested, - this, &BinEditorDocument::provideNewRange); - connect(m_widget, &BinEditorWidget::dataChanged, - this, &IDocument::contentsChanged); + EditorService *es = m_widget->editorService(); + es->setFetchDataHandler([this](quint64 address) { provideData(address); }); + es->setNewRangeRequestHandler([this](quint64 offset) { provideNewRange(offset); }); + es->setDataChangedHandler([this](quint64, const QByteArray &) { contentsChanged(); }); } QByteArray contents() const override @@ -316,7 +291,7 @@ public: return OpenResult::ReadError; } - void provideData(quint64 block) + void provideData(quint64 address) { const FileName fn = filePath(); if (fn.isEmpty()) @@ -324,13 +299,13 @@ public: QFile file(fn.toString()); if (file.open(QIODevice::ReadOnly)) { int blockSize = m_widget->dataBlockSize(); - file.seek(block * blockSize); + file.seek(address); QByteArray data = file.read(blockSize); file.close(); const int dataSize = data.size(); if (dataSize != blockSize) data += QByteArray(blockSize - dataSize, 0); - m_widget->addData(block, data); + m_widget->addData(address, data); } else { QMessageBox::critical(ICore::mainWindow(), tr("File Error"), tr("Cannot open %1: %2").arg( @@ -470,12 +445,30 @@ IEditor *BinEditorFactory::createEditor() return editor; } +///////////////////////////////// BinEditor Services ////////////////////////////////// + +EditorService *FactoryServiceImpl::createEditorService(const QString &title0, bool wantsEditor) +{ + BinEditorWidget *widget = nullptr; + if (wantsEditor) { + QString title = title0; + IEditor *editor = EditorManager::openEditorWithContents( + Core::Constants::K_DEFAULT_BINARY_EDITOR_ID, &title); + if (!editor) + return 0; + widget = qobject_cast<BinEditorWidget *>(editor->widget()); + widget->setEditor(editor); + } else { + widget = new BinEditorWidget; + widget->setWindowTitle(title0); + } + return widget->editorService(); +} ///////////////////////////////// BinEditorPlugin ////////////////////////////////// BinEditorPlugin::BinEditorPlugin() { - m_undoAction = m_redoAction = m_copyAction = m_selectAllAction = 0; } BinEditorPlugin::~BinEditorPlugin() @@ -522,8 +515,8 @@ bool BinEditorPlugin::initialize(const QStringList &arguments, QString *errorMes connect(Core::EditorManager::instance(), &EditorManager::currentEditorChanged, this, &BinEditorPlugin::updateCurrentEditor); + addAutoReleasedObject(new FactoryServiceImpl); addAutoReleasedObject(new BinEditorFactory(this)); - addAutoReleasedObject(new BinEditorWidgetFactory); return true; } diff --git a/src/plugins/bineditor/bineditorplugin.h b/src/plugins/bineditor/bineditorplugin.h index 4fd3c743f8..f22c6701a7 100644 --- a/src/plugins/bineditor/bineditorplugin.h +++ b/src/plugins/bineditor/bineditorplugin.h @@ -25,6 +25,8 @@ #pragma once +#include "bineditorservice.h" + #include <extensionsystem/iplugin.h> #include <coreplugin/editormanager/ieditorfactory.h> #include <coreplugin/icontext.h> @@ -34,9 +36,9 @@ #include <QAction> namespace BinEditor { -class BinEditorWidget; - namespace Internal { + +class BinEditorWidget; class BinEditorFactory; class BinEditorPlugin : public ExtensionSystem::IPlugin @@ -65,10 +67,10 @@ private: Core::Context m_context; QAction *registerNewAction(Core::Id id, const QString &title = QString()); - QAction *m_undoAction; - QAction *m_redoAction; - QAction *m_copyAction; - QAction *m_selectAllAction; + QAction *m_undoAction = nullptr; + QAction *m_redoAction = nullptr; + QAction *m_copyAction = nullptr; + QAction *m_selectAllAction = nullptr; QPointer<BinEditorWidget> m_currentEditor; }; @@ -86,5 +88,14 @@ private: BinEditorPlugin *m_owner; }; +class FactoryServiceImpl : public QObject, public FactoryService +{ + Q_OBJECT + Q_INTERFACES(BinEditor::FactoryService) + +public: + EditorService *createEditorService(const QString &title0, bool wantsEditor) override; +}; + } // namespace Internal } // namespace BinEditor diff --git a/src/plugins/bineditor/bineditorservice.h b/src/plugins/bineditor/bineditorservice.h new file mode 100644 index 0000000000..5a68907e03 --- /dev/null +++ b/src/plugins/bineditor/bineditorservice.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "bineditor_global.h" + +#include <QColor> +#include <QObject> +#include <QString> + +#include <functional> + +namespace Core { class IEditor; } + +namespace BinEditor { + +class EditorService +{ +public: + virtual ~EditorService() {} + + virtual QWidget *widget() = 0; + virtual Core::IEditor *editor() = 0; + + // "Slots" + virtual void setSizes(quint64 address, qint64 range, int blockSize) = 0; + virtual void setReadOnly(bool on) = 0; + virtual void setFinished() = 0; + virtual void setNewWindowRequestAllowed(bool on) = 0; + virtual void setCursorPosition(qint64 pos) = 0; + virtual void updateContents() = 0; + virtual void addData(quint64 address, const QByteArray &data) = 0; + + virtual void clearMarkup() = 0; + virtual void addMarkup(quint64 address, quint64 len, const QColor &color, const QString &toolTip) = 0; + virtual void commitMarkup() = 0; + + // "Signals" + virtual void setFetchDataHandler(const std::function<void(quint64 block)> &) = 0; + virtual void setNewWindowRequestHandler(const std::function<void(quint64 address)> &) = 0; + virtual void setNewRangeRequestHandler(const std::function<void(quint64 address)> &) = 0; + virtual void setDataChangedHandler(const std::function<void(quint64 address, const QByteArray &)> &) = 0; + virtual void setWatchPointRequestHandler(const std::function<void(quint64 address, uint size)> &) = 0; + virtual void setAboutToBeDestroyedHandler(const std::function<void()> &) = 0; +}; + +class FactoryService +{ +public: + virtual ~FactoryService() {} + + // Create a BinEditor widget. Embed into a Core::IEditor iff wantsEditor == true. + virtual EditorService *createEditorService(const QString &title, bool wantsEditor) = 0; +}; + +} // namespace BinEditor + +#define BinEditor_FactoryService_iid "org.qt-project.Qt.Creator.BinEditor.EditorService" + +QT_BEGIN_NAMESPACE +Q_DECLARE_INTERFACE(BinEditor::FactoryService, BinEditor_FactoryService_iid) +QT_END_NAMESPACE diff --git a/src/plugins/bineditor/bineditorwidget.cpp b/src/plugins/bineditor/bineditorwidget.cpp index 44e9ccb694..c6269a6bc0 100644 --- a/src/plugins/bineditor/bineditorwidget.cpp +++ b/src/plugins/bineditor/bineditorwidget.cpp @@ -24,11 +24,17 @@ ****************************************************************************/ #include "bineditorwidget.h" +#include "bineditorservice.h" +#include "markup.h" + +#include <coreplugin/coreconstants.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/ieditor.h> #include <texteditor/fontsettings.h> #include <texteditor/texteditorconstants.h> #include <texteditor/texteditorsettings.h> -#include <coreplugin/editormanager/ieditor.h> + #include <utils/fileutils.h> #include <utils/qtcassert.h> @@ -51,6 +57,8 @@ #include <QToolTip> #include <QWheelEvent> +using namespace Core; + static QByteArray calculateHexPattern(const QByteArray &pattern) { QByteArray result; @@ -69,9 +77,65 @@ static QByteArray calculateHexPattern(const QByteArray &pattern) } namespace BinEditor { +namespace Internal { + +class BinEditorWidgetPrivate : public EditorService +{ +public: + BinEditorWidgetPrivate(BinEditorWidget *widget) : q(widget) {} + ~BinEditorWidgetPrivate() override { if (m_aboutToBeDestroyedHandler) m_aboutToBeDestroyedHandler(); } + + QWidget *widget() override { return q; } + IEditor *editor() override { return q->editor(); } + + void setReadOnly(bool on) override { q->setReadOnly(on); } + void setNewWindowRequestAllowed(bool on) override { q->setNewWindowRequestAllowed(on); } + + void setFinished() override + { + q->setReadOnly(true); + m_fetchDataHandler = {}; + m_newWindowRequestHandler = {}; + m_newRangeRequestHandler = {}; + m_dataChangedHandler = {}; + m_watchPointRequestHandler = {}; + } + + void setSizes(quint64 address, qint64 range, int blockSize) override { q->setSizes(address, range, blockSize); } + void setCursorPosition(qint64 pos) override { q->setCursorPosition(pos); } + void updateContents() override { q->updateContents(); } + void addData(quint64 address, const QByteArray &data) override { q->addData(address, data); } + + void clearMarkup() override { m_markup.clear(); } + void addMarkup(quint64 a, quint64 l, const QColor &c, const QString &t) override { m_markup.append(Markup(a, l, c, t)); } + void commitMarkup() override { q->setMarkup(m_markup); } + + void setFetchDataHandler(const std::function<void(quint64)> &cb) override { m_fetchDataHandler = cb; } + void setNewWindowRequestHandler(const std::function<void(quint64)> &cb) { m_newWindowRequestHandler = cb; } + void setNewRangeRequestHandler(const std::function<void(quint64)> &cb) { m_newRangeRequestHandler = cb; } + void setDataChangedHandler(const std::function<void(quint64, const QByteArray &)> &cb) { m_dataChangedHandler = cb; } + void setWatchPointRequestHandler(const std::function<void(quint64, uint)> &cb) { m_watchPointRequestHandler = cb; } + void setAboutToBeDestroyedHandler(const std::function<void()> & cb) { m_aboutToBeDestroyedHandler = cb; } + + void fetchData(quint64 address) { if (m_fetchDataHandler) m_fetchDataHandler(address); } + void requestNewWindow(quint64 address) { if (m_newWindowRequestHandler) m_newWindowRequestHandler(address); } + void requestWatchPoint(quint64 address, int size) { if (m_watchPointRequestHandler) m_watchPointRequestHandler(address, size); } + void requestNewRange(quint64 address) { if (m_newRangeRequestHandler) m_newRangeRequestHandler(address); } + void announceChangedData(quint64 address, const QByteArray &ba) { if (m_dataChangedHandler) m_dataChangedHandler(address, ba); } + +private: + BinEditorWidget *q; + std::function<void(quint64)> m_fetchDataHandler; + std::function<void(quint64)> m_newWindowRequestHandler; + std::function<void(quint64)> m_newRangeRequestHandler; + std::function<void(quint64, const QByteArray &)> m_dataChangedHandler; + std::function<void(quint64, uint)> m_watchPointRequestHandler; + std::function<void()> m_aboutToBeDestroyedHandler; + QList<Markup> m_markup; +}; BinEditorWidget::BinEditorWidget(QWidget *parent) - : QAbstractScrollArea(parent) + : QAbstractScrollArea(parent), d(new BinEditorWidgetPrivate(this)) { m_bytesPerLine = 16; m_ieditor = 0; @@ -102,6 +166,12 @@ BinEditorWidget::BinEditorWidget(QWidget *parent) BinEditorWidget::~BinEditorWidget() { + delete d; +} + +EditorService *BinEditorWidget::editorService() const +{ + return d; } void BinEditorWidget::init() @@ -153,10 +223,9 @@ void BinEditorWidget::init() } -void BinEditorWidget::addData(quint64 block, const QByteArray &data) +void BinEditorWidget::addData(quint64 addr, const QByteArray &data) { QTC_ASSERT(data.size() == m_blockSize, return); - const quint64 addr = block * m_blockSize; if (addr >= m_baseAddr && addr <= m_baseAddr + m_size - 1) { if (m_data.size() * m_blockSize >= 64 * 1024 * 1024) m_data.clear(); @@ -176,13 +245,11 @@ bool BinEditorWidget::requestDataAt(qint64 pos) const it = m_data.find(block); if (it != m_data.end()) return true; - if (!m_requests.contains(block)) { - m_requests.insert(block); - emit const_cast<BinEditorWidget*>(this)-> - dataRequested(m_baseAddr / m_blockSize + block); - return true; - } - return false; + if (m_requests.contains(block)) + return false; + m_requests.insert(block); + d->fetchData((m_baseAddr / m_blockSize + block) * m_blockSize); + return true; } bool BinEditorWidget::requestOldDataAt(qint64 pos) const @@ -215,7 +282,7 @@ void BinEditorWidget::changeDataAt(qint64 pos, char c) } } - emit dataChanged(m_baseAddr + pos, QByteArray(1, c)); + d->announceChangedData(m_baseAddr + pos, QByteArray(1, c)); } QByteArray BinEditorWidget::dataMid(qint64 from, int length, bool old) const @@ -429,9 +496,9 @@ void BinEditorWidget::scrollContentsBy(int dx, int dy) const QScrollBar * const scrollBar = verticalScrollBar(); const int scrollPos = scrollBar->value(); if (dy <= 0 && scrollPos == scrollBar->maximum()) - emit newRangeRequested(baseAddress() + m_size); + d->requestNewRange(baseAddress() + m_size); else if (dy >= 0 && scrollPos == scrollBar->minimum()) - emit newRangeRequested(baseAddress()); + d->requestNewRange(baseAddress()); } void BinEditorWidget::changeEvent(QEvent *e) @@ -1044,7 +1111,7 @@ bool BinEditorWidget::event(QEvent *e) const QScrollBar * const scrollBar = verticalScrollBar(); const int maximum = scrollBar->maximum(); if (maximum && scrollBar->value() >= maximum - 1) { - emit newRangeRequested(baseAddress() + m_size); + d->requestNewRange(baseAddress() + m_size); return true; } break; @@ -1531,11 +1598,11 @@ void BinEditorWidget::contextMenuEvent(QContextMenuEvent *event) else if (action == jumpToLeAddressHereAction) jumpToAddress(leAddress); else if (action == jumpToBeAddressNewWindowAction) - emit newWindowRequested(beAddress); + d->requestNewWindow(beAddress); else if (action == jumpToLeAddressNewWindowAction) - emit newWindowRequested(leAddress); + d->requestNewWindow(leAddress); else if (action == addWatchpointAction) - emit addWatchpointRequested(m_baseAddr + selStart, byteCount); + d->requestWatchPoint(m_baseAddr + selStart, byteCount); delete contextMenu; } @@ -1557,7 +1624,7 @@ void BinEditorWidget::jumpToAddress(quint64 address) if (address >= m_baseAddr && address < m_baseAddr + m_size) setCursorPosition(address - m_baseAddr); else - emit newRangeRequested(address); + d->requestNewRange(address); } void BinEditorWidget::setNewWindowRequestAllowed(bool c) @@ -1614,4 +1681,5 @@ void BinEditorWidget::setMarkup(const QList<Markup> &markup) viewport()->update(); } +} // namespace Internal } // namespace BinEditor diff --git a/src/plugins/bineditor/bineditorwidget.h b/src/plugins/bineditor/bineditorwidget.h index dd54b53c7f..de8d78462c 100644 --- a/src/plugins/bineditor/bineditorwidget.h +++ b/src/plugins/bineditor/bineditorwidget.h @@ -27,6 +27,7 @@ #include "bineditor_global.h" #include "markup.h" +#include "bineditorservice.h" #include <QBasicTimer> #include <QMap> @@ -46,8 +47,11 @@ namespace Core { class IEditor; } namespace TextEditor { class FontSettings; } namespace BinEditor { +namespace Internal { -class BINEDITOR_EXPORT BinEditorWidget : public QAbstractScrollArea +class BinEditorWidgetPrivate; + +class BinEditorWidget : public QAbstractScrollArea { Q_OBJECT Q_PROPERTY(bool modified READ isModified WRITE setModified DESIGNABLE false) @@ -59,17 +63,19 @@ public: BinEditorWidget(QWidget *parent = 0); ~BinEditorWidget(); + EditorService *editorService() const; + quint64 baseAddress() const { return m_baseAddr; } - Q_INVOKABLE void setSizes(quint64 startAddr, qint64 range, int blockSize = 4096); + void setSizes(quint64 startAddr, qint64 range, int blockSize = 4096); int dataBlockSize() const { return m_blockSize; } QByteArray contents() const { return dataMid(0, m_size); } - Q_INVOKABLE void addData(quint64 block, const QByteArray &data); + void addData(quint64 addr, const QByteArray &data); bool newWindowRequestAllowed() const { return m_canRequestNewWindow; } - Q_INVOKABLE void updateContents(); + void updateContents(); bool save(QString *errorString, const QString &oldFileName, const QString &newFileName); void zoomIn(int range = 1); @@ -81,7 +87,7 @@ public: }; qint64 cursorPosition() const; - Q_INVOKABLE void setCursorPosition(qint64 pos, MoveMode moveMode = MoveAnchor); + void setCursorPosition(qint64 pos, MoveMode moveMode = MoveAnchor); void jumpToAddress(quint64 address); void setModified(bool); @@ -123,19 +129,13 @@ public: void setMarkup(const QList<Markup> &markup); void setNewWindowRequestAllowed(bool c); -Q_SIGNALS: +signals: void modificationChanged(bool modified); void undoAvailable(bool); void redoAvailable(bool); void cursorPositionChanged(int position); - void dataRequested(quint64 block); - void newWindowRequested(quint64 address); - void newRangeRequested(quint64 address); - void addWatchpointRequested(quint64 address, uint size); - void dataChanged(quint64 address, const QByteArray &data); - -protected: +private: void scrollContentsBy(int dx, int dy); void paintEvent(QPaintEvent *e); void resizeEvent(QResizeEvent *); @@ -150,7 +150,9 @@ protected: void timerEvent(QTimerEvent *); void contextMenuEvent(QContextMenuEvent *event); -private: + friend class BinEditorWidgetPrivate; + BinEditorWidgetPrivate *d; + typedef QMap<qint64, QByteArray> BlockMap; BlockMap m_data; BlockMap m_oldData; @@ -242,4 +244,5 @@ private: QList<Markup> m_markup; }; +} // namespace Internal } // namespace BinEditor diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 9287b5c3fa..a1ec4a0824 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -151,13 +151,11 @@ static const char localsPrefixC[] = "local."; struct MemoryViewCookie { - explicit MemoryViewCookie(MemoryAgent *a = 0, QObject *e = 0, - quint64 addr = 0, quint64 l = 0) : - agent(a), editorToken(e), address(addr), length(l) + explicit MemoryViewCookie(MemoryAgent *a = 0, quint64 addr = 0, quint64 l = 0) + : agent(a), address(addr), length(l) {} MemoryAgent *agent; - QObject *editorToken; quint64 address; quint64 length; }; @@ -1499,11 +1497,11 @@ void CdbEngine::handleResolveSymbolHelper(const QList<quint64> &addresses, Disas } } -void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length) +void CdbEngine::fetchMemory(MemoryAgent *agent, quint64 addr, quint64 length) { if (debug) qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr); - const MemoryViewCookie cookie(agent, editor, addr, length); + const MemoryViewCookie cookie(agent, addr, length); if (m_accessible) postFetchMemory(cookie); else @@ -1521,7 +1519,7 @@ void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie) if (response.resultClass == ResultDone && cookie.agent) { const QByteArray data = QByteArray::fromHex(response.data.data().toUtf8()); if (unsigned(data.size()) == cookie.length) - cookie.agent->addLazyData(cookie.editorToken, cookie.address, data); + cookie.agent->addData(cookie.address, data); } else { showMessage(response.data["msg"].data(), LogWarning); } @@ -1529,7 +1527,7 @@ void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie) runCommand(cmd); } -void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data) +void CdbEngine::changeMemory(MemoryAgent *, quint64 addr, const QByteArray &data) { QTC_ASSERT(!data.isEmpty(), return); if (!m_accessible) { diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index 264bf9ec25..9d6c5d5238 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -99,9 +99,8 @@ public: void attemptBreakpointSynchronization() override; void fetchDisassembler(DisassemblerAgent *agent) override; - void fetchMemory(MemoryAgent *, QObject *, quint64 addr, quint64 length) override; - void changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, - const QByteArray &data) override; + void fetchMemory(MemoryAgent *, quint64 addr, quint64 length) override; + void changeMemory(MemoryAgent *, quint64 addr, const QByteArray &data) override; void reloadModules() override; void loadSymbols(const QString &moduleName) override; diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro index f014c8512a..c8888989d3 100644 --- a/src/plugins/debugger/debugger.pro +++ b/src/plugins/debugger/debugger.pro @@ -59,7 +59,6 @@ HEADERS += \ watchdelegatewidgets.h \ debuggertooltipmanager.h \ debuggersourcepathmappingwidget.h \ - memoryview.h \ localsandexpressionswindow.h \ imageviewer.h \ simplifytype.h \ @@ -109,7 +108,6 @@ SOURCES += \ watchdelegatewidgets.cpp \ debuggertooltipmanager.cpp \ debuggersourcepathmappingwidget.cpp \ - memoryview.cpp \ localsandexpressionswindow.cpp \ imageviewer.cpp \ simplifytype.cpp \ diff --git a/src/plugins/debugger/debugger.qbs b/src/plugins/debugger/debugger.qbs index 8e53a7cd55..b80189782d 100644 --- a/src/plugins/debugger/debugger.qbs +++ b/src/plugins/debugger/debugger.qbs @@ -35,7 +35,8 @@ Project { cpp.enableExceptions: true pluginRecommends: [ - "CppEditor" + "CppEditor", + "BinEditor" ] Group { @@ -73,7 +74,6 @@ Project { "localsandexpressionswindow.cpp", "localsandexpressionswindow.h", "logwindow.cpp", "logwindow.h", "memoryagent.cpp", "memoryagent.h", - "memoryview.cpp", "memoryview.h", "moduleshandler.cpp", "moduleshandler.h", "outputcollector.cpp", "outputcollector.h", "procinterrupt.cpp", "procinterrupt.h", diff --git a/src/plugins/debugger/debugger_dependencies.pri b/src/plugins/debugger/debugger_dependencies.pri index 7c0f491116..09789cf780 100644 --- a/src/plugins/debugger/debugger_dependencies.pri +++ b/src/plugins/debugger/debugger_dependencies.pri @@ -15,7 +15,8 @@ QTC_PLUGIN_DEPENDS += \ qtsupport \ texteditor QTC_PLUGIN_RECOMMENDS += \ - cppeditor + cppeditor \ + bineditor QTC_TEST_DEPENDS += \ qmakeprojectmanager diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 8bc8518d8e..607d8613ea 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -154,6 +154,59 @@ void LocationMark::dragToLine(int line) ////////////////////////////////////////////////////////////////////// // +// MemoryAgentSet +// +////////////////////////////////////////////////////////////////////// + +class MemoryAgentSet +{ +public: + ~MemoryAgentSet() + { + qDeleteAll(m_agents); + m_agents.clear(); + } + + // Called by engine to create a new view. + void createBinEditor(const MemoryViewSetupData &data, DebuggerEngine *engine) + { + auto agent = new MemoryAgent(data, engine); + if (agent->isUsable()) { + m_agents.append(agent); + } else { + delete agent; + AsynchronousMessageBox::warning( + DebuggerEngine::tr("No Memory Viewer Available"), + DebuggerEngine::tr("The memory contents cannot be shown as no viewer plugin " + "for binary data has been loaded.")); + } + } + + // On stack frame completed and on request. + void updateContents() + { + foreach (MemoryAgent *agent, m_agents) { + if (agent) + agent->updateContents(); + } + } + + void handleDebuggerFinished() + { + foreach (MemoryAgent *agent, m_agents) { + if (agent) + agent->setFinished(); // Prevent triggering updates, etc. + } + } + +private: + QList<MemoryAgent *> m_agents; +}; + + + +////////////////////////////////////////////////////////////////////// +// // DebuggerEnginePrivate // ////////////////////////////////////////////////////////////////////// @@ -182,7 +235,7 @@ public: m_threadsHandler(engine), m_watchHandler(engine), m_disassemblerAgent(engine), - m_memoryAgent(engine) + m_isStateDebugging(false) { connect(&m_locationTimer, &QTimer::timeout, this, &DebuggerEnginePrivate::resetLocation); @@ -320,7 +373,7 @@ public: QFutureInterface<void> m_progress; DisassemblerAgent m_disassemblerAgent; - MemoryAgent m_memoryAgent; + MemoryAgentSet m_memoryAgents; QScopedPointer<LocationMark> m_locationMark; QTimer m_locationTimer; @@ -480,15 +533,13 @@ QAbstractItemModel *DebuggerEngine::sourceFilesModel() const return sourceFilesHandler()->model(); } -void DebuggerEngine::fetchMemory(MemoryAgent *, QObject *, - quint64 addr, quint64 length) +void DebuggerEngine::fetchMemory(MemoryAgent *, quint64 addr, quint64 length) { Q_UNUSED(addr); Q_UNUSED(length); } -void DebuggerEngine::changeMemory(MemoryAgent *, QObject *, - quint64 addr, const QByteArray &data) +void DebuggerEngine::changeMemory(MemoryAgent *, quint64 addr, const QByteArray &data) { Q_UNUSED(addr); Q_UNUSED(data); @@ -618,8 +669,6 @@ void DebuggerEngine::gotoLocation(const Location &loc) if (loc.needsMarker()) d->m_locationMark.reset(new LocationMark(this, file, line)); - - //qDebug() << "MEMORY: " << d->m_memoryAgent.hasVisibleEditor(); } // Called from RunControl. @@ -1270,7 +1319,7 @@ void DebuggerEngine::setState(DebuggerState state, bool forced) foreach (Breakpoint bp, breakHandler()->engineBreakpoints(this)) bp.notifyBreakpointReleased(); DebuggerToolTipManager::deregisterEngine(this); - d->m_memoryAgent.handleDebuggerFinished(); + d->m_memoryAgents.handleDebuggerFinished(); prepareForRestart(); } @@ -1798,12 +1847,12 @@ void DebuggerEngine::showStoppedByExceptionMessageBox(const QString &description void DebuggerEngine::openMemoryView(const MemoryViewSetupData &data) { - d->m_memoryAgent.createBinEditor(data); + d->m_memoryAgents.createBinEditor(data, this); } void DebuggerEngine::updateMemoryViews() { - d->m_memoryAgent.updateContents(); + d->m_memoryAgents.updateContents(); } void DebuggerEngine::openDisassemblerView(const Location &location) @@ -2005,7 +2054,7 @@ void DebuggerEngine::updateLocalsView(const GdbMi &all) const bool partial = all["partial"].toInt(); if (!partial) - emit stackFrameCompleted(); + updateMemoryViews(); } bool DebuggerEngine::canHandleToolTip(const DebuggerToolTipContext &context) const diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index 9fab50b063..cf5acf4db1 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -217,10 +217,8 @@ public: virtual void runCommand(const DebuggerCommand &cmd); virtual void openMemoryView(const MemoryViewSetupData &data); - virtual void fetchMemory(Internal::MemoryAgent *, QObject *, - quint64 addr, quint64 length); - virtual void changeMemory(Internal::MemoryAgent *, QObject *, - quint64 addr, const QByteArray &data); + virtual void fetchMemory(MemoryAgent *, quint64 addr, quint64 length); + virtual void changeMemory(MemoryAgent *, quint64 addr, const QByteArray &data); virtual void updateMemoryViews(); virtual void openDisassemblerView(const Internal::Location &location); virtual void fetchDisassembler(Internal::DisassemblerAgent *); @@ -328,8 +326,6 @@ public: signals: void stateChanged(Debugger::DebuggerState state); - // A new stack frame is on display including locals. - void stackFrameCompleted(); /* * For "external" clients of a debugger run control that needs to do * further setup before the debugger is started (e.g. RemoteLinux). diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index adc486fb39..31c4eb82c6 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -3710,39 +3710,34 @@ void GdbEngine::handleWatchPoint(const DebuggerResponse &response) class MemoryAgentCookie { public: - MemoryAgentCookie() - : accumulator(0), pendingRequests(0), agent(0), token(0), base(0), offset(0), length(0) - {} + MemoryAgentCookie() {} -public: - QByteArray *accumulator; // Shared between split request. Last one cleans up. - uint *pendingRequests; // Shared between split request. Last one cleans up. + QByteArray *accumulator = nullptr; // Shared between split request. Last one cleans up. + uint *pendingRequests = nullptr; // Shared between split request. Last one cleans up. QPointer<MemoryAgent> agent; - QPointer<QObject> token; - quint64 base; // base address. - uint offset; // offset to base, and in accumulator - uint length; // + quint64 base = 0; // base address. + uint offset = 0; // offset to base, and in accumulator + uint length = 0; // }; -void GdbEngine::changeMemory(MemoryAgent *, QObject *, - quint64 addr, const QByteArray &data) +void GdbEngine::changeMemory(MemoryAgent *agent, quint64 addr, const QByteArray &data) { + Q_UNUSED(agent) DebuggerCommand cmd("-data-write-memory 0x" + QString::number(addr, 16) + " d 1", NeedsStop); foreach (unsigned char c, data) cmd.function += ' ' + QString::number(uint(c)); + cmd.callback = CB(handleVarAssign); runCommand(cmd); } -void GdbEngine::fetchMemory(MemoryAgent *agent, QObject *token, quint64 addr, - quint64 length) +void GdbEngine::fetchMemory(MemoryAgent *agent, quint64 addr, quint64 length) { MemoryAgentCookie ac; ac.accumulator = new QByteArray(length, char()); ac.pendingRequests = new uint(1); ac.agent = agent; - ac.token = token; ac.base = addr; ac.length = length; fetchMemoryHelper(ac); @@ -3800,7 +3795,7 @@ void GdbEngine::handleFetchMemory(const DebuggerResponse &response, MemoryAgentC } if (*ac.pendingRequests <= 0) { - ac.agent->addLazyData(ac.token, ac.base, *ac.accumulator); + ac.agent->addData(ac.base, *ac.accumulator); delete ac.pendingRequests; delete ac.accumulator; } diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index 25184fd12c..6fdfd10b1a 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -370,12 +370,10 @@ protected: virtual void assignValueInDebugger(WatchItem *item, const QString &expr, const QVariant &value) override; - virtual void fetchMemory(MemoryAgent *agent, QObject *token, - quint64 addr, quint64 length) override; + void fetchMemory(MemoryAgent *agent, quint64 addr, quint64 length) override; void fetchMemoryHelper(const MemoryAgentCookie &cookie); void handleChangeMemory(const DebuggerResponse &response); - virtual void changeMemory(MemoryAgent *agent, QObject *token, - quint64 addr, const QByteArray &data) override; + void changeMemory(MemoryAgent *agent, quint64 addr, const QByteArray &data) override; void handleFetchMemory(const DebuggerResponse &response, MemoryAgentCookie ac); virtual void watchPoint(const QPoint &) override; diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 92b4ae97a2..19761ba6de 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -1054,45 +1054,26 @@ void LldbEngine::fetchFullBacktrace() runCommand(cmd); } -void LldbEngine::fetchMemory(MemoryAgent *agent, QObject *editorToken, - quint64 addr, quint64 length) +void LldbEngine::fetchMemory(MemoryAgent *agent, quint64 addr, quint64 length) { - int id = m_memoryAgents.value(agent, -1); - if (id == -1) { - id = ++m_lastAgentId; - m_memoryAgents.insert(agent, id); - } - m_memoryAgentTokens.insert(id, editorToken); - DebuggerCommand cmd("fetchMemory"); cmd.arg("address", addr); cmd.arg("length", length); - cmd.callback = [this, id](const DebuggerResponse &response) { + cmd.callback = [this, agent](const DebuggerResponse &response) { qulonglong addr = response.data["address"].toAddress(); - QPointer<MemoryAgent> agent = m_memoryAgents.key(id); - if (!agent.isNull()) { - QPointer<QObject> token = m_memoryAgentTokens.value(id); - QTC_ASSERT(!token.isNull(), return); - QByteArray ba = QByteArray::fromHex(response.data["contents"].data().toUtf8()); - agent->addLazyData(token.data(), addr, ba); - } + QByteArray ba = QByteArray::fromHex(response.data["contents"].data().toUtf8()); + agent->addData(addr, ba); }; runCommand(cmd); } -void LldbEngine::changeMemory(MemoryAgent *agent, QObject *editorToken, - quint64 addr, const QByteArray &data) +void LldbEngine::changeMemory(MemoryAgent *agent, quint64 addr, const QByteArray &data) { - int id = m_memoryAgents.value(agent, -1); - if (id == -1) { - id = ++m_lastAgentId; - m_memoryAgents.insert(agent, id); - m_memoryAgentTokens.insert(id, editorToken); - } + Q_UNUSED(agent) DebuggerCommand cmd("writeMemory"); cmd.arg("address", addr); cmd.arg("data", QString::fromUtf8(data.toHex())); - cmd.callback = [this, id](const DebuggerResponse &response) { Q_UNUSED(response); }; + cmd.callback = [this](const DebuggerResponse &response) { Q_UNUSED(response); }; runCommand(cmd); } diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h index b55927f1c6..8129cc4042 100644 --- a/src/plugins/debugger/lldb/lldbengine.h +++ b/src/plugins/debugger/lldb/lldbengine.h @@ -118,8 +118,8 @@ private: bool isSynchronous() const override { return true; } void setRegisterValue(const QString &name, const QString &value) override; - void fetchMemory(Internal::MemoryAgent *, QObject *, quint64 addr, quint64 length) override; - void changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data) override; + void fetchMemory(MemoryAgent *, quint64 addr, quint64 length) override; + void changeMemory(MemoryAgent *, quint64 addr, const QByteArray &data) override; QString errorMessage(QProcess::ProcessError error) const; bool hasCapability(unsigned cap) const override; @@ -156,8 +156,6 @@ private: int m_lastAgentId; int m_continueAtNextSpontaneousStop; QMap<QPointer<DisassemblerAgent>, int> m_disassemblerAgents; - QMap<QPointer<MemoryAgent>, int> m_memoryAgents; - QHash<int, QPointer<QObject> > m_memoryAgentTokens; QHash<int, DebuggerCommand> m_commandForToken; diff --git a/src/plugins/debugger/memoryagent.cpp b/src/plugins/debugger/memoryagent.cpp index 21c3a12d41..5fc78867c4 100644 --- a/src/plugins/debugger/memoryagent.cpp +++ b/src/plugins/debugger/memoryagent.cpp @@ -24,13 +24,15 @@ ****************************************************************************/ #include "memoryagent.h" -#include "memoryview.h" #include "breakhandler.h" #include "debuggerengine.h" #include "debuggerstartparameters.h" #include "debuggercore.h" #include "debuggerinternalconstants.h" +#include "registerhandler.h" + +#include <bineditor/bineditorservice.h> #include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/ieditor.h> @@ -42,13 +44,131 @@ #include <extensionsystem/pluginmanager.h> #include <extensionsystem/invoker.h> +#include <QVBoxLayout> + #include <cstring> using namespace Core; +using namespace ProjectExplorer; namespace Debugger { namespace Internal { +enum { BinBlockSize = 1024 }; +enum { DataRange = 1024 * 1024 }; + +/*! + \class Debugger::Internal::MemoryView + \brief The MemoryView class is a base class for memory view tool windows. + + This class is a small tool-window that stays on top and displays a chunk + of memory based on the widget provided by the BinEditor plugin. + + \sa Debugger::Internal::MemoryAgent, Debugger::DebuggerEngine +*/ + +class MemoryView : public QWidget +{ +public: + explicit MemoryView(MemoryAgent *agent, QWidget *parent) + : QWidget(parent, Qt::Tool), m_agent(agent) + { + setAttribute(Qt::WA_DeleteOnClose); + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(agent->service()->widget()); + layout->setContentsMargins(0, 0, 0, 0); + setMinimumWidth(400); + resize(800, 200); + } + + void updateContents() + { + if (m_agent) + m_agent->updateContents(); + } + +protected: + void setAddress(quint64 a) + { + if (m_agent) + m_agent->setAddress(a); + } + + void setMarkup(const QList<MemoryMarkup> &m) + { + if (m_agent) + m_agent->setMarkup(m); + } + +private: + QPointer<MemoryAgent> m_agent; +}; + + +/*! + \class Debugger::Internal::RegisterMemoryView + \brief The RegisterMemoryView class provides a 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 +*/ + +class RegisterMemoryView : public MemoryView +{ +public: + RegisterMemoryView(MemoryAgent *agent, quint64 addr, const QString ®Name, + RegisterHandler *rh, QWidget *parent) + : MemoryView(agent, parent), m_registerName(regName), m_registerAddress(addr) + { + connect(rh, &QAbstractItemModel::modelReset, this, &QWidget::close); + connect(rh, &RegisterHandler::registerChanged, this, &RegisterMemoryView::onRegisterChanged); + updateContents(); + } + +private: + void onRegisterChanged(const QString &name, quint64 value) + { + if (name == m_registerName) + setRegisterAddress(value); + } + + void setRegisterAddress(quint64 v) + { + if (v == m_registerAddress) { + updateContents(); + return; + } + m_registerAddress = v; + setAddress(v); + setWindowTitle(registerViewTitle(m_registerName, v)); + if (v) + setMarkup(registerViewMarkup(v, m_registerName)); + } + + QString m_registerName; + quint64 m_registerAddress; +}; + +QString registerViewTitle(const QString ®isterName, quint64 addr) +{ + return MemoryAgent::tr("Memory at Register \"%1\" (0x%2)").arg(registerName).arg(addr, 0, 16); +} + +QList<MemoryMarkup> registerViewMarkup(quint64 a, const QString ®Name) +{ + return { MemoryMarkup(a, 1, QColor(Qt::blue).lighter(), + MemoryAgent::tr("Register \"%1\"").arg(regName)) }; +} + /////////////////////////////////////////////////////////////////////// // // MemoryAgent @@ -80,219 +200,137 @@ namespace Internal { \sa Debugger::MemoryView, Debugger::RegisterMemoryView */ -MemoryAgent::MemoryAgent(DebuggerEngine *engine) - : QObject(engine), m_engine(engine) +BinEditor::FactoryService *binEditorFactory() { - QTC_CHECK(engine); - connect(engine, &DebuggerEngine::stackFrameCompleted, - this, &MemoryAgent::updateContents); + static auto theBinEditorFactory = ExtensionSystem::PluginManager::getObject<BinEditor::FactoryService>(); + return theBinEditorFactory; } -MemoryAgent::~MemoryAgent() +bool MemoryAgent::hasBinEditor() { - closeEditors(); - closeViews(); + return binEditorFactory() != nullptr; } -void MemoryAgent::closeEditors() +MemoryAgent::MemoryAgent(const MemoryViewSetupData &data, DebuggerEngine *engine) + : m_engine(engine), m_flags(data.flags) { - if (m_editors.isEmpty()) + auto factory = binEditorFactory(); + if (!factory) return; - QSet<IDocument *> documents; - foreach (QPointer<IEditor> editor, m_editors) - if (editor) - documents.insert(editor->document()); - EditorManager::closeDocuments(documents.toList()); - m_editors.clear(); -} + const bool readOnly = (m_flags & DebuggerEngine::MemoryReadOnly) != 0; + QString title = data.title.isEmpty() ? tr("Memory at 0x%1").arg(data.startAddress, 0, 16) : data.title; + if (!(m_flags & DebuggerEngine::MemoryView) && !title.endsWith('$')) + title.append(" $"); -void MemoryAgent::closeViews() -{ - foreach (const QPointer<MemoryView> &w, m_views) - if (w) - w->close(); - m_views.clear(); -} + if (m_flags & DebuggerEngine::MemoryView) { + // Ask BIN editor plugin for factory service and have it create a bin editor widget. + m_service = factory->createEditorService(title, false); + } else { + // Editor: Register tracking not supported. + m_service = factory->createEditorService(title, true); + } -void MemoryAgent::updateMemoryView(quint64 address, quint64 length) -{ - m_engine->fetchMemory(this, sender(), address, length); -} + if (!m_service) + return; -void MemoryAgent::connectBinEditorWidget(QWidget *w) -{ - connect(w, SIGNAL(dataRequested(quint64)), SLOT(fetchLazyData(quint64))); - connect(w, SIGNAL(newWindowRequested(quint64)), SLOT(createBinEditor(quint64))); - connect(w, SIGNAL(newRangeRequested(quint64)), SLOT(provideNewRange(quint64))); - connect(w, SIGNAL(dataChanged(quint64,QByteArray)), SLOT(handleDataChanged(quint64,QByteArray))); - connect(w, SIGNAL(dataChanged(quint64,QByteArray)), SLOT(handleDataChanged(quint64,QByteArray))); - connect(w, SIGNAL(addWatchpointRequested(quint64,uint)), SLOT(handleWatchpointRequest(quint64,uint))); -} + m_service->setNewRangeRequestHandler([this](quint64 address) { + m_service->setSizes(address, DataRange, BinBlockSize); + }); + + m_service->setFetchDataHandler([this](quint64 address) { + m_engine->fetchMemory(this, address, BinBlockSize); + }); + + m_service->setNewWindowRequestHandler([this](quint64 address) { + MemoryViewSetupData data; + data.startAddress = address; + auto agent = new MemoryAgent(data, m_engine); + if (!agent->isUsable()) + delete agent; + }); + + m_service->setDataChangedHandler([this](quint64 address, const QByteArray &data) { + m_engine->changeMemory(this, address, data); + }); + + m_service->setWatchPointRequestHandler([this](quint64 address, uint size) { + m_engine->breakHandler()->setWatchpointAtAddress(address, size); + }); + + m_service->setAboutToBeDestroyedHandler([this] { m_service = nullptr; }); -bool MemoryAgent::doCreateBinEditor(const MemoryViewSetupData &data) -{ - const bool readOnly = (data.flags & DebuggerEngine::MemoryReadOnly) != 0; - QString title = data.title.isEmpty() ? tr("Memory at 0x%1").arg(data.startAddress, 0, 16) : data.title; // Separate view? - if (data.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::getObjectByClassName(QLatin1String("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; + if (m_flags & DebuggerEngine::MemoryView) { // Memory view tracking register value, providing its own updating mechanism. - if (data.flags & DebuggerEngine::MemoryTrackRegister) { - topLevel = new RegisterMemoryView(binEditor, data.startAddress, data.registerName, m_engine->registerHandler(), data.parent); + if (m_flags & DebuggerEngine::MemoryTrackRegister) { + auto view = new RegisterMemoryView(this, data.startAddress, data.registerName, m_engine->registerHandler(), data.parent); + view->show(); } else { // Ordinary memory view - MemoryView::setBinEditorMarkup(binEditor, data.markup); - MemoryView::setBinEditorRange(binEditor, data.startAddress, MemoryAgent::DataRange, MemoryAgent::BinBlockSize); - topLevel = new MemoryView(binEditor, data.parent); - topLevel->setWindowTitle(title); + auto view = new MemoryView(this, data.parent); + view->setWindowTitle(title); + view->show(); } - m_views << topLevel; - topLevel->show(); - return true; + } else { + m_service->editor()->document()->setTemporary(true); + m_service->editor()->document()->setProperty(Constants::OPENED_BY_DEBUGGER, QVariant(true)); } - // Editor: Register tracking not supported. - QTC_ASSERT(!(data.flags & DebuggerEngine::MemoryTrackRegister), return false); - if (!title.endsWith(QLatin1Char('$'))) - title.append(QLatin1String(" $")); - IEditor *editor = EditorManager::openEditorWithContents( - Core::Constants::K_DEFAULT_BINARY_EDITOR_ID, &title); - if (!editor) - return false; - editor->document()->setProperty(Constants::OPENED_BY_DEBUGGER, QVariant(true)); - editor->document()->setTemporary(true); - QWidget *editorBinEditor = editor->widget(); - connectBinEditorWidget(editorBinEditor); - MemoryView::setBinEditorReadOnly(editorBinEditor, readOnly); - MemoryView::setBinEditorNewWindowRequestAllowed(editorBinEditor, true); - MemoryView::setBinEditorRange(editorBinEditor, data.startAddress, MemoryAgent::DataRange, MemoryAgent::BinBlockSize); - MemoryView::setBinEditorMarkup(editorBinEditor, data.markup); - m_editors << editor; - return true; -} -void MemoryAgent::createBinEditor(const MemoryViewSetupData &data) -{ - if (!doCreateBinEditor(data)) - AsynchronousMessageBox::warning( - tr("No Memory Viewer Available"), - tr("The memory contents cannot be shown as no viewer plugin " - "for binary data has been loaded.")); + m_service->setReadOnly(readOnly); + m_service->setNewWindowRequestAllowed(true); + m_service->setSizes(data.startAddress, DataRange, BinBlockSize); + setMarkup(data.markup); } -void MemoryAgent::createBinEditor(quint64 addr) -{ - MemoryViewSetupData data; - data.startAddress = addr; - createBinEditor(data); -} - -void MemoryAgent::fetchLazyData(quint64 block) -{ - m_engine->fetchMemory(this, sender(), BinBlockSize * block, BinBlockSize); -} - -void MemoryAgent::addLazyData(QObject *editorToken, quint64 addr, - const QByteArray &ba) -{ - QWidget *w = qobject_cast<QWidget *>(editorToken); - QTC_ASSERT(w, return); - MemoryView::binEditorAddData(w, addr, ba); -} - -void MemoryAgent::provideNewRange(quint64 address) +MemoryAgent::~MemoryAgent() { - QWidget *w = qobject_cast<QWidget *>(sender()); - QTC_ASSERT(w, return); - MemoryView::setBinEditorRange(w, address, DataRange, BinBlockSize); + if (m_service) { + if (m_service->editor()) + EditorManager::closeDocument(m_service->editor()->document()); + if (m_service->widget()) + m_service->widget()->close(); + } } -void MemoryAgent::handleDataChanged(quint64 addr, const QByteArray &data) +void MemoryAgent::setAddress(quint64 address) { - m_engine->changeMemory(this, sender(), addr, data); + QTC_ASSERT(m_service, return); + m_service->setSizes(address, DataRange, BinBlockSize); } -void MemoryAgent::handleWatchpointRequest(quint64 address, uint size) +void MemoryAgent::setMarkup(const QList<MemoryMarkup> &markup) { - m_engine->breakHandler()->setWatchpointAtAddress(address, size); + QTC_ASSERT(m_service, return); + m_service->clearMarkup(); + for (const MemoryMarkup &m : markup) + m_service->addMarkup(m.address, m.length, m.color, m.toolTip); + m_service->commitMarkup(); } void MemoryAgent::updateContents() { - foreach (const QPointer<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(); + if (!(m_flags & DebuggerEngine::MemoryTrackRegister) && m_service) + m_service->updateContents(); } -bool MemoryAgent::hasVisibleEditor() const +void MemoryAgent::addData(quint64 address, const QByteArray &a) { - QList<IEditor *> visible = EditorManager::visibleEditors(); - foreach (QPointer<IEditor> editor, m_editors) - if (visible.contains(editor.data())) - return true; - return false; + QTC_ASSERT(m_service, return); + m_service->addData(address, a); } -void MemoryAgent::handleDebuggerFinished() +void MemoryAgent::setFinished() { - foreach (const QPointer<IEditor> &editor, m_editors) { - if (editor) { // Prevent triggering updates, etc. - MemoryView::setBinEditorReadOnly(editor->widget(), true); - editor->widget()->disconnect(this); - } - } -} - -bool MemoryAgent::isBigEndian(const ProjectExplorer::Abi &a) -{ - switch (a.architecture()) { - case ProjectExplorer::Abi::UnknownArchitecture: - case ProjectExplorer::Abi::X86Architecture: - case ProjectExplorer::Abi::ItaniumArchitecture: // Configureable - case ProjectExplorer::Abi::ArmArchitecture: // Configureable - case ProjectExplorer::Abi::ShArchitecture: // Configureable - break; - case ProjectExplorer::Abi::MipsArchitecture: // Configureable - case ProjectExplorer::Abi::PowerPCArchitecture: // Configureable - return true; - } - return false; -} - -// Read a POD variable from a memory location. Swap bytes if endianness differs -template <class POD> POD readPod(const unsigned char *data, bool swapByteOrder) -{ - POD pod = 0; - if (swapByteOrder) { - unsigned char *target = reinterpret_cast<unsigned char *>(&pod) + sizeof(POD) - 1; - for (size_t i = 0; i < sizeof(POD); i++) - *target-- = data[i]; - } else { - std::memcpy(&pod, data, sizeof(POD)); - } - return pod; + if (m_service) + m_service->setFinished(); } -// Read memory from debuggee -quint64 MemoryAgent::readInferiorPointerValue(const unsigned char *data, const ProjectExplorer::Abi &a) +bool MemoryAgent::isUsable() { - const bool swapByteOrder = isBigEndian(a) != isBigEndian(ProjectExplorer::Abi::hostAbi()); - return a.wordWidth() == 32 ? readPod<quint32>(data, swapByteOrder) : - readPod<quint64>(data, swapByteOrder); + return m_service != nullptr; } } // namespace Internal diff --git a/src/plugins/debugger/memoryagent.h b/src/plugins/debugger/memoryagent.h index e765f465ce..2197510c1a 100644 --- a/src/plugins/debugger/memoryagent.h +++ b/src/plugins/debugger/memoryagent.h @@ -32,25 +32,23 @@ #include <QPointer> #include <QColor> -namespace Core { class IEditor; } - -namespace ProjectExplorer { class Abi; } +namespace BinEditor { class EditorService; } namespace Debugger { namespace Internal { class DebuggerEngine; -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) {} + MemoryMarkup() {} + MemoryMarkup(quint64 address, quint64 length, QColor c, const QString &tt) + : address(address), length(length), color(c), toolTip(tt) + {} - quint64 address; - quint64 length; + quint64 address = 0; + quint64 length = 0; QColor color; QString toolTip; }; @@ -58,13 +56,13 @@ public: class MemoryViewSetupData { public: - MemoryViewSetupData() : parent(0), startAddress(0), flags(0) {} + MemoryViewSetupData() {} - QWidget *parent; - quint64 startAddress; + QWidget *parent = nullptr; + quint64 startAddress = 0; QString registerName; - unsigned flags; - QList<Internal::MemoryMarkup> markup; + unsigned flags = 0; + QList<MemoryMarkup> markup; QPoint pos; QString title; }; @@ -74,44 +72,30 @@ class MemoryAgent : public QObject Q_OBJECT public: - explicit MemoryAgent(DebuggerEngine *engine); + MemoryAgent(const MemoryViewSetupData &data, DebuggerEngine *engine); ~MemoryAgent(); - enum { BinBlockSize = 1024 }; - enum { DataRange = 1024 * 1024 }; - - bool hasVisibleEditor() const; - - static bool isBigEndian(const ProjectExplorer::Abi &a); - static quint64 readInferiorPointerValue(const unsigned char *data, const ProjectExplorer::Abi &a); - -public slots: - // Called by engine to create a new view. - void createBinEditor(const MemoryViewSetupData &data); - void createBinEditor(quint64 startAddr); - // Called by engine to create a tooltip. - void addLazyData(QObject *editorToken, quint64 addr, const QByteArray &data); - // On stack frame completed and on request. + void setAddress(quint64 address); + void setMarkup(const QList<MemoryMarkup> &ml); void updateContents(); - void closeEditors(); - void closeViews(); - void handleDebuggerFinished(); + void addData(quint64 address, const QByteArray &data); + void setFinished(); + bool isUsable(); + + BinEditor::EditorService *service() { return m_service; } -private slots: - void fetchLazyData(quint64 block); - void provideNewRange(quint64 address); - void handleDataChanged(quint64 address, const QByteArray &data); - void handleWatchpointRequest(quint64 address, uint size); - void updateMemoryView(quint64 address, quint64 length); + static bool hasBinEditor(); private: - void connectBinEditorWidget(QWidget *w); - bool doCreateBinEditor(const MemoryViewSetupData &data); + // The backend, provided by the BinEditor plugin, if loaded. + BinEditor::EditorService *m_service = nullptr; - QList<QPointer<Core::IEditor> > m_editors; - QList<QPointer<MemoryView> > m_views; - QPointer<DebuggerEngine> m_engine; + DebuggerEngine *m_engine = nullptr; + int m_flags = 0; }; +QList<MemoryMarkup> registerViewMarkup(quint64 address, const QString ®Name); +QString registerViewTitle(const QString ®isterName, quint64 address = 0); + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/memoryview.cpp b/src/plugins/debugger/memoryview.cpp deleted file mode 100644 index f80ee06098..0000000000 --- a/src/plugins/debugger/memoryview.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "memoryview.h" -#include "registerhandler.h" -#include "memoryagent.h" - -#include <bineditor/markup.h> - -#include <QVBoxLayout> -#include <QDebug> - -namespace Debugger { -namespace Internal { - -/*! - \class Debugger::Internal::MemoryView - \brief The MemoryView class is a base class for memory view tool windows. - - This class is a 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), m_binEditor(binEditor) -{ - setAttribute(Qt::WA_DeleteOnClose); - QVBoxLayout *layout = new QVBoxLayout(this); - layout->addWidget(binEditor); - layout->setContentsMargins(0, 0, 0, 0); - setMinimumWidth(400); - resize(800, 200); -} - -void MemoryView::setBinEditorRange(QWidget *w, quint64 address, qint64 range, int blockSize) -{ - QMetaObject::invokeMethod(w, "setSizes", - Q_ARG(quint64, address), Q_ARG(qint64, 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, qint64 pos) -{ - QMetaObject::invokeMethod(w, "setCursorPosition", Q_ARG(qint64, pos)); -} - -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 The RegisterMemoryView class provides a 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, quint64 addr, - const QString ®Name, - RegisterHandler *handler, QWidget *parent) : - MemoryView(binEditor, parent), - m_registerName(regName), m_registerAddress(addr) -{ - connect(handler, &QAbstractItemModel::modelReset, this, &QWidget::close); - connect(handler, &RegisterHandler::registerChanged, this, &RegisterMemoryView::onRegisterChanged); - updateContents(); -} - -void RegisterMemoryView::onRegisterChanged(const QString &name, quint64 value) -{ - if (name == m_registerName) - setRegisterAddress(value); -} - -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) -{ - return { MemoryMarkup(a, 1, QColor(Qt::blue).lighter(), tr("Register \"%1\"").arg(regName)) }; -} - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/memoryview.h b/src/plugins/debugger/memoryview.h deleted file mode 100644 index 10cfb5013e..0000000000 --- a/src/plugins/debugger/memoryview.h +++ /dev/null @@ -1,81 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <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, qint64 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, qint64 pos); - 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, quint64 addr, const QString ®Name, - RegisterHandler *rh, QWidget *parent = 0); - - static QList<MemoryMarkup> registerMarkup(quint64 a, const QString ®Name); - static QString title(const QString ®isterName, quint64 a = 0); - -private: - void onRegisterChanged(const QString &name, quint64 value); - void setRegisterAddress(quint64 v); - - QString m_registerName; - quint64 m_registerAddress; -}; - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/qml/qmlcppengine.cpp b/src/plugins/debugger/qml/qmlcppengine.cpp index b9f616f703..5d53735b7f 100644 --- a/src/plugins/debugger/qml/qmlcppengine.cpp +++ b/src/plugins/debugger/qml/qmlcppengine.cpp @@ -129,10 +129,14 @@ void QmlCppEngine::watchPoint(const QPoint &point) m_cppEngine->watchPoint(point); } -void QmlCppEngine::fetchMemory(MemoryAgent *ma, QObject *obj, - quint64 addr, quint64 length) +void QmlCppEngine::fetchMemory(MemoryAgent *agent, quint64 addr, quint64 length) { - m_cppEngine->fetchMemory(ma, obj, addr, length); + m_cppEngine->fetchMemory(agent, addr, length); +} + +void QmlCppEngine::changeMemory(MemoryAgent *agent, quint64 addr, const QByteArray &data) +{ + m_cppEngine->changeMemory(agent, addr, data); } void QmlCppEngine::fetchDisassembler(DisassemblerAgent *da) diff --git a/src/plugins/debugger/qml/qmlcppengine.h b/src/plugins/debugger/qml/qmlcppengine.h index 53be72b4b7..52beaa5dcb 100644 --- a/src/plugins/debugger/qml/qmlcppengine.h +++ b/src/plugins/debugger/qml/qmlcppengine.h @@ -47,7 +47,8 @@ public: void selectWatchData(const QString &iname) override; void watchPoint(const QPoint &) override; - void fetchMemory(MemoryAgent *, QObject *, quint64 addr, quint64 length) override; + void fetchMemory(MemoryAgent *, quint64 addr, quint64 length) override; + void changeMemory(MemoryAgent *, quint64 addr, const QByteArray &data) override; void fetchDisassembler(DisassemblerAgent *) override; void activateFrame(int index) override; diff --git a/src/plugins/debugger/registerhandler.cpp b/src/plugins/debugger/registerhandler.cpp index 6d6a49f29d..41b92dff90 100644 --- a/src/plugins/debugger/registerhandler.cpp +++ b/src/plugins/debugger/registerhandler.cpp @@ -28,7 +28,6 @@ #include "debuggerengine.h" #include "watchdelegatewidgets.h" -#include "memoryview.h" #include "memoryagent.h" #include "debuggeractions.h" #include "debuggerdialogs.h" @@ -712,8 +711,8 @@ bool RegisterHandler::contextMenuEvent(const ItemViewEvent &ev) MemoryViewSetupData data; data.startAddress = address; data.registerName = registerName; - data.markup = RegisterMemoryView::registerMarkup(address, registerName); - data.title = RegisterMemoryView::title(registerName); + data.markup = registerViewMarkup(address, registerName); + data.title = registerViewTitle(registerName); m_engine->openMemoryView(data); }); diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index 442bd199a9..88c2272f18 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -35,7 +35,6 @@ #include "debuggertooltipmanager.h" #include "imageviewer.h" #include "memoryagent.h" -#include "memoryview.h" #include "registerhandler.h" #include "simplifytype.h" #include "watchdelegatewidgets.h" |