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