forked from qt-creator/qt-creator
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>
This commit is contained in:
@@ -3,6 +3,7 @@ include(../../qtcreatorplugin.pri)
|
||||
DEFINES += BINEDITOR_LIBRARY
|
||||
|
||||
HEADERS += bineditorplugin.h \
|
||||
bineditorservice.h \
|
||||
bineditorwidget.h \
|
||||
bineditorconstants.h \
|
||||
bineditor_global.h \
|
||||
|
@@ -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",
|
||||
]
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
85
src/plugins/bineditor/bineditorservice.h
Normal file
85
src/plugins/bineditor/bineditorservice.h
Normal file
@@ -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
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
|
@@ -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 \
|
||||
|
@@ -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",
|
||||
|
@@ -15,7 +15,8 @@ QTC_PLUGIN_DEPENDS += \
|
||||
qtsupport \
|
||||
texteditor
|
||||
QTC_PLUGIN_RECOMMENDS += \
|
||||
cppeditor
|
||||
cppeditor \
|
||||
bineditor
|
||||
QTC_TEST_DEPENDS += \
|
||||
qmakeprojectmanager
|
||||
|
||||
|
@@ -152,6 +152,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
|
||||
|
@@ -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).
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
bool MemoryAgent::hasBinEditor()
|
||||
{
|
||||
return binEditorFactory() != nullptr;
|
||||
}
|
||||
|
||||
MemoryAgent::MemoryAgent(const MemoryViewSetupData &data, DebuggerEngine *engine)
|
||||
: m_engine(engine), m_flags(data.flags)
|
||||
{
|
||||
auto factory = binEditorFactory();
|
||||
if (!factory)
|
||||
return;
|
||||
|
||||
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(" $");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (!m_service)
|
||||
return;
|
||||
|
||||
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; });
|
||||
|
||||
// Separate view?
|
||||
if (m_flags & DebuggerEngine::MemoryView) {
|
||||
// Memory view tracking register value, providing its own updating mechanism.
|
||||
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
|
||||
auto view = new MemoryView(this, data.parent);
|
||||
view->setWindowTitle(title);
|
||||
view->show();
|
||||
}
|
||||
} else {
|
||||
m_service->editor()->document()->setTemporary(true);
|
||||
m_service->editor()->document()->setProperty(Constants::OPENED_BY_DEBUGGER, QVariant(true));
|
||||
}
|
||||
|
||||
m_service->setReadOnly(readOnly);
|
||||
m_service->setNewWindowRequestAllowed(true);
|
||||
m_service->setSizes(data.startAddress, DataRange, BinBlockSize);
|
||||
setMarkup(data.markup);
|
||||
}
|
||||
|
||||
MemoryAgent::~MemoryAgent()
|
||||
{
|
||||
closeEditors();
|
||||
closeViews();
|
||||
}
|
||||
|
||||
void MemoryAgent::closeEditors()
|
||||
{
|
||||
if (m_editors.isEmpty())
|
||||
return;
|
||||
|
||||
QSet<IDocument *> documents;
|
||||
foreach (QPointer<IEditor> editor, m_editors)
|
||||
if (editor)
|
||||
documents.insert(editor->document());
|
||||
EditorManager::closeDocuments(documents.toList());
|
||||
m_editors.clear();
|
||||
}
|
||||
|
||||
void MemoryAgent::closeViews()
|
||||
{
|
||||
foreach (const QPointer<MemoryView> &w, m_views)
|
||||
if (w)
|
||||
w->close();
|
||||
m_views.clear();
|
||||
}
|
||||
|
||||
void MemoryAgent::updateMemoryView(quint64 address, quint64 length)
|
||||
{
|
||||
m_engine->fetchMemory(this, sender(), address, length);
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
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;
|
||||
// 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);
|
||||
} 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);
|
||||
}
|
||||
m_views << topLevel;
|
||||
topLevel->show();
|
||||
return true;
|
||||
if (m_service) {
|
||||
if (m_service->editor())
|
||||
EditorManager::closeDocument(m_service->editor()->document());
|
||||
if (m_service->widget())
|
||||
m_service->widget()->close();
|
||||
}
|
||||
// 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)
|
||||
void MemoryAgent::setAddress(quint64 address)
|
||||
{
|
||||
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."));
|
||||
QTC_ASSERT(m_service, return);
|
||||
m_service->setSizes(address, DataRange, BinBlockSize);
|
||||
}
|
||||
|
||||
void MemoryAgent::createBinEditor(quint64 addr)
|
||||
void MemoryAgent::setMarkup(const QList<MemoryMarkup> &markup)
|
||||
{
|
||||
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)
|
||||
{
|
||||
QWidget *w = qobject_cast<QWidget *>(sender());
|
||||
QTC_ASSERT(w, return);
|
||||
MemoryView::setBinEditorRange(w, address, DataRange, BinBlockSize);
|
||||
}
|
||||
|
||||
void MemoryAgent::handleDataChanged(quint64 addr, const QByteArray &data)
|
||||
{
|
||||
m_engine->changeMemory(this, sender(), addr, data);
|
||||
}
|
||||
|
||||
void MemoryAgent::handleWatchpointRequest(quint64 address, uint size)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (m_service)
|
||||
m_service->setFinished();
|
||||
}
|
||||
|
||||
bool MemoryAgent::isBigEndian(const ProjectExplorer::Abi &a)
|
||||
bool MemoryAgent::isUsable()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Read memory from debuggee
|
||||
quint64 MemoryAgent::readInferiorPointerValue(const unsigned char *data, const ProjectExplorer::Abi &a)
|
||||
{
|
||||
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
|
||||
|
@@ -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();
|
||||
|
||||
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);
|
||||
BinEditor::EditorService *service() { return m_service; }
|
||||
|
||||
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
|
||||
|
@@ -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
|
@@ -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
|
@@ -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)
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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);
|
||||
});
|
||||
|
||||
|
@@ -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"
|
||||
|
Reference in New Issue
Block a user