Commit 89ffffc1 authored by hjk's avatar hjk

debugger: use the bineditor to show memory dumps

parent 0c6b754a
......@@ -39,6 +39,7 @@ Debugging
* CDB: Fixed thread handling
* CDB: Added internal dumpers for string types for debuggee crashes
* Improved QObject dumping, print out QRect/QSize, enumerations and flags
* Use the BinEditor plugin to show raw memory
Designer
* Added support for rearranging and floating form editor tools
......
......@@ -32,14 +32,13 @@
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorconstants.h>
#include <QtGui/QScrollBar>
#include <QtCore/QByteArrayMatcher>
#include <QtGui/QApplication>
#include <QtGui/QClipboard>
#include <QtGui/QFontMetrics>
#include <QtGui/QPainter>
#include <QtGui/QScrollBar>
#include <QtGui/QWheelEvent>
#include <QtGui/QApplication>
#include <QtGui/QClipboard>
#include <QtCore/QByteArrayMatcher>
using namespace BINEditor;
......@@ -794,7 +793,7 @@ void BinEditor::paintEvent(QPaintEvent *e)
}
}
if (cursor >= 0) {
if (cursor >= 0 && !printable.isEmpty()) {
QRect cursorRect(text_x + painter.fontMetrics().width(printable.left(cursor)),
y-m_ascent,
painter.fontMetrics().width(printable.at(cursor)),
......
......@@ -30,12 +30,13 @@
#ifndef BINEDITOR_H
#define BINEDITOR_H
#include <QtGui/qabstractscrollarea.h>
#include <QtCore/qbasictimer.h>
#include <QtCore/qstack.h>
#include <QtCore/qset.h>
#include <QtGui/qtextdocument.h>
#include <QtGui/qtextformat.h>
#include <QtCore/QBasicTimer>
#include <QtCore/QSet>
#include <QtCore/QStack>
#include <QtGui/QAbstractScrollArea>
#include <QtGui/QTextDocument>
#include <QtGui/QTextFormat>
namespace Core {
class IEditor;
......@@ -63,9 +64,9 @@ public:
inline int dataSize() const { return m_size; }
inline bool inLazyMode() const { return m_inLazyMode; }
void setLazyData(int cursorPosition, int size, int blockSize = 4096);
Q_INVOKABLE void setLazyData(int cursorPosition, int size, int blockSize = 4096);
inline int lazyDataBlockSize() const { return m_blockSize; }
void addLazyData(int block, const QByteArray &data);
Q_INVOKABLE void addLazyData(int block, const QByteArray &data);
bool applyModifications(QByteArray &data) const;
void zoomIn(int range = 1);
......
......@@ -33,7 +33,7 @@
#include <extensionsystem/iplugin.h>
#include <coreplugin/editormanager/ieditorfactory.h>
#include <QtCore/qplugin.h>
#include <QtCore/QtPlugin>
#include <QtCore/QPointer>
#include <QtCore/QStringList>
#include <QtGui/QAction>
......
......@@ -6,11 +6,11 @@ TARGET = Debugger
# CONFIG += single
include(../../qtcreatorplugin.pri)
include(../../plugins/projectexplorer/projectexplorer.pri)
include(../../plugins/find/find.pri)
include(../../plugins/coreplugin/coreplugin.pri)
include(../../plugins/texteditor/texteditor.pri)
include(../../plugins/cpptools/cpptools.pri)
include(../../plugins/find/find.pri)
include(../../plugins/projectexplorer/projectexplorer.pri)
include(../../plugins/texteditor/texteditor.pri)
include(../../libs/cplusplus/cplusplus.pri)
include(../../libs/utils/utils.pri)
INCLUDEPATH += $$PWD/../../libs/utils
......@@ -20,6 +20,7 @@ QT += gui network script
HEADERS += \
breakhandler.h \
breakwindow.h \
debuggeragents.h \
debuggeractions.h \
debuggerconstants.h \
debuggerdialogs.h \
......@@ -50,6 +51,7 @@ SOURCES += \
breakhandler.cpp \
breakwindow.cpp \
breakwindow.h \
debuggeragents.cpp \
debuggeractions.cpp \
debuggerdialogs.cpp \
debuggermanager.cpp \
......
......@@ -37,6 +37,7 @@
QT_BEGIN_NAMESPACE
class QActionGroup;
QT_END_NAMESPACE
namespace Debugger {
namespace Internal {
......@@ -80,6 +81,12 @@ enum DebuggerActionCode
LockView,
LogTimeStamps,
RecheckDebuggingHelpers,
UseDebuggingHelpers,
UseCustomDebuggingHelperLocation,
CustomDebuggingHelperLocation,
DebugDebuggingHelpers,
// Gdb
GdbLocation,
GdbEnvironment,
......@@ -99,12 +106,6 @@ enum DebuggerActionCode
AssignValue,
AssignType,
RecheckDebuggingHelpers,
UseDebuggingHelpers,
UseCustomDebuggingHelperLocation,
CustomDebuggingHelperLocation,
DebugDebuggingHelpers,
// Source List
ListSourceFiles,
......
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://www.qtsoftware.com/contact.
**
**************************************************************************/
#include "debuggeragents.h"
#include "idebuggerengine.h"
#include <coreplugin/editormanager/editormanager.h>
namespace Debugger {
namespace Internal {
MemoryViewAgent::MemoryViewAgent(DebuggerManager *manager, quint64 addr)
: QObject(manager), m_engine(manager->currentEngine())
{
Core::EditorManager *editorManager = Core::EditorManager::instance();
QString titlePattern = "Memory $";
m_editor = editorManager->openEditorWithContents(
Core::Constants::K_DEFAULT_BINARY_EDITOR,
&titlePattern);
connect(m_editor->widget(), SIGNAL(lazyDataRequested(int,bool)),
this, SLOT(fetchLazyData(int,bool)));
editorManager->activateEditor(m_editor);
QMetaObject::invokeMethod(m_editor->widget(), "setLazyData",
Q_ARG(int, addr), Q_ARG(int, INT_MAX), Q_ARG(int, BinBlockSize));
}
MemoryViewAgent::~MemoryViewAgent()
{
m_editor->deleteLater();
}
void MemoryViewAgent::fetchLazyData(int block, bool sync)
{
Q_UNUSED(sync); // FIXME: needed support for incremental searching
m_engine->fetchMemory(this, BinBlockSize * block, BinBlockSize);
}
void MemoryViewAgent::addLazyData(quint64 addr, const QByteArray &ba)
{
QMetaObject::invokeMethod(m_editor->widget(), "addLazyData",
Q_ARG(int, addr / BinBlockSize), Q_ARG(QByteArray, ba));
}
} // namespace Internal
} // namespace Debugger
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://www.qtsoftware.com/contact.
**
**************************************************************************/
#ifndef DEBUGGER_AGENTS_H
#define DEBUGGER_AGENTS_H
#include "debuggermanager.h"
#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/ieditor.h>
#include <utils/qtcassert.h>
#include <QtCore/QObject>
#include <QtCore/QDebug>
#include <QtCore/QPointer>
#include <QtGui/QAction>
namespace Debugger {
namespace Internal {
class DebuggerManager;
// Object 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.
class MemoryViewAgent : public QObject
{
Q_OBJECT
public:
// Called from Gui
MemoryViewAgent(DebuggerManager *manager, quint64 startaddr);
~MemoryViewAgent();
enum { BinBlockSize = 1024 };
public slots:
// Called from Engine
void addLazyData(quint64 addr, const QByteArray &data);
// Called from Editor
void fetchLazyData(int block, bool sync);
public:
QPointer<IDebuggerEngine> m_engine;
QPointer<Core::IEditor> m_editor;
};
} // namespace Internal
} // namespace Debugger
#endif // DEBUGGER_WATCHWINDOW_H
......@@ -171,6 +171,7 @@ private:
Ui::StartRemoteDialog *m_ui;
};
} // namespace Debugger
} // namespace Internal
......
......@@ -226,9 +226,9 @@ void DebuggerManager::init()
m_stackWindow = new StackWindow;
m_sourceFilesWindow = new SourceFilesWindow;
m_threadsWindow = new ThreadsWindow;
m_localsWindow = new WatchWindow(WatchWindow::LocalsType);
m_watchersWindow = new WatchWindow(WatchWindow::WatchersType);
//m_tooltipWindow = new WatchWindow(WatchWindow::TooltipType);
m_localsWindow = new WatchWindow(WatchWindow::LocalsType, this);
m_watchersWindow = new WatchWindow(WatchWindow::WatchersType, this);
//m_tooltipWindow = new WatchWindow(WatchWindow::TooltipType, this);
m_statusTimer = new QTimer(this);
m_mainWindow = new Core::Utils::FancyMainWindow;
......@@ -1497,7 +1497,6 @@ bool DebuggerManager::isReverseDebugging() const
return m_reverseDirectionAction->isChecked();
}
//////////////////////////////////////////////////////////////////////
//
// Testing
......
......@@ -251,8 +251,7 @@ private:
// DebuggerManager
//
class DebuggerManager : public QObject,
public IDebuggerManagerAccessForEngines
class DebuggerManager : public QObject, public IDebuggerManagerAccessForEngines
{
Q_OBJECT
......@@ -265,9 +264,11 @@ public:
IDebuggerManagerAccessForEngines *engineInterface();
Core::Utils::FancyMainWindow *mainWindow() const { return m_mainWindow; }
QLabel *statusLabel() const { return m_statusLabel; }
IDebuggerEngine *currentEngine() const { return m_engine; }
public slots:
void startNewDebugger(DebuggerRunControl *runControl, const QSharedPointer<DebuggerStartParameters> &startParameters);
void startNewDebugger(DebuggerRunControl *runControl,
const QSharedPointer<DebuggerStartParameters> &startParameters);
void exitDebugger();
virtual QSharedPointer<DebuggerStartParameters> startParameters() const;
......@@ -297,6 +298,7 @@ public slots:
void setBreakpoint(const QString &fileName, int lineNumber);
void activateFrame(int index);
void selectThread(int index);
void fetchMemory(quint64 addr, quint64 length);
void stepExec();
void stepOutExec();
......@@ -406,15 +408,12 @@ signals:
void gotoLocationRequested(const QString &file, int line, bool setLocationMarker);
void resetLocationRequested();
void currentTextEditorRequested(QString *fileName, int *lineNumber, QObject **ob);
void currentMainWindowRequested(QWidget **);
void sessionValueRequested(const QString &name, QVariant *value);
void setSessionValueRequested(const QString &name, const QVariant &value);
void configValueRequested(const QString &name, QVariant *value);
void setConfigValueRequested(const QString &name, const QVariant &value);
void applicationOutputAvailable(const QString &output);
public:
private:
void init();
void runTest(const QString &fileName);
......
......@@ -88,6 +88,8 @@
#include <QtGui/QTextCursor>
#include <QtGui/QMessageBox>
#include <climits>
using namespace Core;
using namespace Debugger::Constants;
using namespace Debugger::Internal;
......@@ -407,6 +409,7 @@ void DebuggingHelperOptionPage::updateState()
} // namespace Internal
} // namespace Debugger
///////////////////////////////////////////////////////////////////////
//
// DebuggerPlugin
......@@ -1114,6 +1117,7 @@ void DebuggerPlugin::gotoLocation(const QString &fileName, int lineNumber,
}
}
void DebuggerPlugin::changeStatus(int status)
{
bool startIsContinue = (status == DebuggerInferiorStopped);
......
......@@ -34,6 +34,7 @@
#include "watchutils.h"
#include "debuggeractions.h"
#include "debuggeragents.h"
#include "debuggerconstants.h"
#include "debuggermanager.h"
#include "debuggertooltip.h"
......@@ -3987,6 +3988,40 @@ void GdbEngine::handleWatchPoint(const GdbResultRecord &record, const QVariant &
}
}
void GdbEngine::handleFetchMemory(const GdbResultRecord &record,
const QVariant &cookie)
{
// ^done,addr="0x08910c88",nr-bytes="16",total-bytes="16",
// next-row="0x08910c98",prev-row="0x08910c78",next-page="0x08910c98",
// prev-page="0x08910c78",memory=[{addr="0x08910c88",
// data=["1","0","0","0","5","0","0","0","0","0","0","0","0","0","0","0"]}]
bool ok = true;
MemoryViewAgent *agent = (MemoryViewAgent *)cookie.toULongLong(&ok);
QTC_ASSERT(ok, return);
QTC_ASSERT(agent, return);
QByteArray ba;
GdbMi memory = record.data.findChild("memory");
QTC_ASSERT(memory.children().size() == 1, return);
GdbMi memory0 = memory.children().at(0); // we asked for only one 'row'
quint64 addr = memory0.findChild("addr").data().toULongLong(&ok, 0);
QTC_ASSERT(ok, return);
GdbMi data = memory0.findChild("data");
foreach (const GdbMi &child, data.children()) {
unsigned char c = child.data().toUInt(&ok, 0);
QTC_ASSERT(ok, return);
ba.append(c);
}
//qDebug() << "GDB READ MEMORY" << agent << addr << data.data() << ba.size();
agent->addLazyData(addr, ba);
}
void GdbEngine::fetchMemory(MemoryViewAgent *agent, quint64 addr, quint64 length)
{
//qDebug() << "GDB MEMORY FETCH" << addr << length;
postCommand(_("-data-read-memory %1 x 1 1 %2").arg(addr).arg(length),
NeedsStop, CB(handleFetchMemory), QVariant(quint64(agent)));
}
IDebuggerEngine *createGdbEngine(DebuggerManager *parent, QList<Core::IOptionsPage*> *opts)
{
opts->push_back(new GdbOptionsPage);
......
......@@ -122,6 +122,9 @@ private:
void loadAllSymbols();
virtual QList<Symbol> moduleSymbols(const QString &moduleName);
void fetchMemory(MemoryViewAgent *agent, quint64 addr, quint64 length);
void handleFetchMemory(const GdbResultRecord &record, const QVariant &cookie);
Q_SLOT void setDebugDebuggingHelpers(const QVariant &on);
Q_SLOT void setUseDebuggingHelpers(const QVariant &on);
......
......@@ -49,6 +49,7 @@ namespace Internal {
class Symbol;
class WatchData;
struct DebuggerStartParameters;
class MemoryViewAgent;
class IDebuggerEngine : public QObject
{
......@@ -95,6 +96,8 @@ public:
virtual void reloadFullStack() = 0;
virtual void watchPoint(const QPoint &) {}
virtual void fetchMemory(MemoryViewAgent *, quint64 addr, quint64 length)
{ Q_UNUSED(addr); Q_UNUSED(length); }
};
} // namespace Internal
......
......@@ -690,6 +690,16 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
return m_handler->m_typeFormats[data.type];
return format;
}
case AddressRole: {
if (!data.addr.isEmpty())
return data.addr;
bool ok;
(void) data.value.toULongLong(&ok, 0);
if (ok)
return data.value;
return QVariant();
}
default:
break;
......
......@@ -146,7 +146,8 @@ enum WatchRoles
ActiveDataRole, // used for tooltip
TypeFormatListRole,
TypeFormatRole, // used to communicate alternative formats to the view
IndividualFormatRole
IndividualFormatRole,
AddressRole, // some memory address related to the object
};
enum IntegerFormat
......
......@@ -31,6 +31,7 @@
#include "watchhandler.h"
#include "debuggeractions.h"
#include "debuggeragents.h"
#include <utils/qtcassert.h>
......@@ -39,8 +40,11 @@
#include <QtGui/QAction>
#include <QtGui/QContextMenuEvent>
#include <QtGui/QDialog>
#include <QtGui/QHBoxLayout>
#include <QtGui/QHeaderView>
#include <QtGui/QItemDelegate>
#include <QtGui/QLabel>
#include <QtGui/QLineEdit>
#include <QtGui/QMenu>
#include <QtGui/QResizeEvent>
......@@ -111,8 +115,9 @@ public:
//
/////////////////////////////////////////////////////////////////////
WatchWindow::WatchWindow(Type type, QWidget *parent)
: QTreeView(parent), m_alwaysResizeColumnsToContents(true), m_type(type)
WatchWindow::WatchWindow(Type type, DebuggerManager *manager, QWidget *parent)
: QTreeView(parent), m_alwaysResizeColumnsToContents(true), m_type(type),
m_manager(manager)
{
m_grabbing = false;
......@@ -237,28 +242,44 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
}
QMenu menu;
QAction *act1 = new QAction(tr("Adjust column widths to contents"), &menu);
QAction *act2 = new QAction(tr("Always adjust column widths to contents"), &menu);
act2->setCheckable(true);
act2->setChecked(m_alwaysResizeColumnsToContents);
//QAction *act4 = theDebuggerAction(WatchExpressionInWindow);
//menu.addAction(act4);
QAction *act3 = new QAction(tr("Insert new watch item"), &menu);
QAction *act4 = new QAction(tr("Select widget to watch"), &menu);
menu.addAction(act1);
menu.addAction(act2);
QAction *actAdjustColumnWidths =
new QAction(tr("Adjust column widths to contents"), &menu);
QAction *actAlwaysAdjustColumnWidth =
new QAction(tr("Always adjust column widths to contents"), &menu);
actAlwaysAdjustColumnWidth->setCheckable(true);
actAlwaysAdjustColumnWidth->setChecked(m_alwaysResizeColumnsToContents);
//QAction *actWatchExpressionInWindow
// = theDebuggerAction(WatchExpressionInWindow);
//menu.addAction(actWatchExpressionInWindow);
QAction *actInsertNewWatchItem =
new QAction(tr("Insert new watch item"), &menu);
QAction *actSelectWidgetToWatch =
new QAction(tr("Select widget to watch"), &menu);
QString address = model()->data(mi0, AddressRole).toString();
QAction *actWatchKnownMemory = 0;
QAction *actWatchUnknownMemory = 0;
if (address.isEmpty())
actWatchUnknownMemory = new QAction(tr("Open memory editor"), &menu);
else
actWatchKnownMemory =
new QAction(tr("Open memory editor at %1").arg(address), &menu);
menu.addAction(actAdjustColumnWidths);
menu.addAction(actAlwaysAdjustColumnWidth);
menu.addSeparator();
int atype = (m_type == LocalsType) ? WatchExpression : RemoveWatchExpression;
menu.addAction(theDebuggerAction(atype)->updatedAction(exp));
menu.addAction(act3);
menu.addAction(act4);
menu.addAction(actInsertNewWatchItem);
menu.addAction(actSelectWidgetToWatch);
menu.addMenu(&typeFormatMenu);
menu.addMenu(&individualFormatMenu);
menu.addAction(actWatchKnownMemory ? actWatchKnownMemory : actWatchUnknownMemory);
menu.addSeparator();
menu.addAction(theDebuggerAction(RecheckDebuggingHelpers));
......@@ -268,14 +289,33 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
QAction *act = menu.exec(ev->globalPos());
if (act == act1) {
if (act == actAdjustColumnWidths) {
resizeColumnsToContents();
} else if (act == act2) {
} else if (act == actAlwaysAdjustColumnWidth) {
setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
} else if (act == act3) {
} else if (act == actInsertNewWatchItem) {
theDebuggerAction(WatchExpression)
->trigger(WatchHandler::watcherEditPlaceHolder());
} else if (act == act4) {
} else if (act == actWatchKnownMemory) {
bool ok = true;
uint addr = address.toUInt(&ok, 0);
(void) new MemoryViewAgent(m_manager, addr);
} else if (act == actWatchUnknownMemory) {
QLabel *label = new QLabel("Enter an address: ");
QLineEdit *lineEdit = new QLineEdit;
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(label);
layout->addWidget(lineEdit);
QDialog dialog(this);
dialog.setWindowTitle("Select start address");
dialog.setLayout(layout);
connect(lineEdit, SIGNAL(returnPressed()), &dialog, SLOT(accept()));
if (dialog.exec() == QDialog::Accepted) {
bool ok = true;
uint addr = lineEdit->text().toUInt(&ok, 0);
(void) new MemoryViewAgent(m_manager, addr);
}
} else if (act == actSelectWidgetToWatch) {
grabMouse(Qt::CrossCursor);
m_grabbing = true;
} else {
......
......@@ -41,6 +41,8 @@ namespace Internal {
//
/////////////////////////////////////////////////////////////////////