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:
hjk
2016-07-14 10:00:15 +02:00
committed by hjk
parent 66142d94d2
commit 6e925910b4
26 changed files with 600 additions and 661 deletions

View File

@@ -3,6 +3,7 @@ include(../../qtcreatorplugin.pri)
DEFINES += BINEDITOR_LIBRARY
HEADERS += bineditorplugin.h \
bineditorservice.h \
bineditorwidget.h \
bineditorconstants.h \
bineditor_global.h \

View File

@@ -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",
]

View File

@@ -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;
}

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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 \

View File

@@ -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",

View File

@@ -15,7 +15,8 @@ QTC_PLUGIN_DEPENDS += \
qtsupport \
texteditor
QTC_PLUGIN_RECOMMENDS += \
cppeditor
cppeditor \
bineditor
QTC_TEST_DEPENDS += \
qmakeprojectmanager

View File

@@ -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

View File

@@ -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).

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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 &regName,
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 &registerName, quint64 addr)
{
return MemoryAgent::tr("Memory at Register \"%1\" (0x%2)").arg(registerName).arg(addr, 0, 16);
}
QList<MemoryMarkup> registerViewMarkup(quint64 a, const QString &regName)
{
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

View File

@@ -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 &regName);
QString registerViewTitle(const QString &registerName, quint64 address = 0);
} // namespace Internal
} // namespace Debugger

View File

@@ -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 &regName,
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 &registerName, 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 &regName)
{
return { MemoryMarkup(a, 1, QColor(Qt::blue).lighter(), tr("Register \"%1\"").arg(regName)) };
}
} // namespace Internal
} // namespace Debugger

View File

@@ -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 &regName,
RegisterHandler *rh, QWidget *parent = 0);
static QList<MemoryMarkup> registerMarkup(quint64 a, const QString &regName);
static QString title(const QString &registerName, 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

View File

@@ -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)

View File

@@ -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;

View File

@@ -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);
});

View File

@@ -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"