Added Breakpoint widget (#546)

* Added breakpoint widget
This commit is contained in:
fcasal 2018-06-22 09:45:00 +01:00 committed by xarkes
parent 2bfa3b7a3f
commit fc94b896c4
9 changed files with 441 additions and 6 deletions

View File

@ -851,6 +851,57 @@ void CutterCore::addBreakpoint(RVA addr)
{ {
cmd("db " + RAddressString(addr)); cmd("db " + RAddressString(addr));
emit instructionChanged(addr); emit instructionChanged(addr);
emit breakpointsChanged();
}
void CutterCore::delBreakpoint(RVA addr)
{
cmd("db- " + RAddressString(addr));
emit instructionChanged(addr);
emit breakpointsChanged();
}
void CutterCore::delAllBreakpoints()
{
cmd("db-*");
emit breakpointsChanged();
}
void CutterCore::enableBreakpoint(RVA addr)
{
cmd("dbe " + RAddressString(addr));
emit instructionChanged(addr);
emit breakpointsChanged();
}
void CutterCore::disableBreakpoint(RVA addr)
{
cmd("dbd " + RAddressString(addr));
emit instructionChanged(addr);
emit breakpointsChanged();
}
QList<BreakpointDescription> CutterCore::getBreakpoints()
{
QList<BreakpointDescription> ret;
QJsonArray breakpointArray = cmdj("dbj").array();
for (QJsonValue value : breakpointArray) {
QJsonObject bpObject = value.toObject();
BreakpointDescription bp;
bp.addr = bpObject["addr"].toVariant().toULongLong();
bp.size = bpObject["size"].toVariant().toInt();
bp.permission = bpObject["prot"].toString();
bp.hw = bpObject["hw"].toBool();
bp.trace = bpObject["trace"].toBool();
bp.enabled = bpObject["enabled"].toBool();
ret << bp;
}
return ret;
} }
QJsonDocument CutterCore::getBacktrace() QJsonDocument CutterCore::getBacktrace()

View File

@ -303,6 +303,15 @@ struct MemoryMapDescription {
QString permission; QString permission;
}; };
struct BreakpointDescription {
RVA addr;
int size;
QString permission;
bool hw;
bool trace;
bool enabled;
};
Q_DECLARE_METATYPE(FunctionDescription) Q_DECLARE_METATYPE(FunctionDescription)
Q_DECLARE_METATYPE(ImportDescription) Q_DECLARE_METATYPE(ImportDescription)
Q_DECLARE_METATYPE(ExportDescription) Q_DECLARE_METATYPE(ExportDescription)
@ -332,6 +341,7 @@ Q_DECLARE_METATYPE(ZignatureDescription)
Q_DECLARE_METATYPE(SearchDescription) Q_DECLARE_METATYPE(SearchDescription)
Q_DECLARE_METATYPE(SectionDescription) Q_DECLARE_METATYPE(SectionDescription)
Q_DECLARE_METATYPE(MemoryMapDescription) Q_DECLARE_METATYPE(MemoryMapDescription)
Q_DECLARE_METATYPE(BreakpointDescription)
class CutterCore: public QObject class CutterCore: public QObject
{ {
@ -476,7 +486,11 @@ public:
void continueUntilDebug(QString offset); void continueUntilDebug(QString offset);
void stepDebug(); void stepDebug();
void stepOverDebug(); void stepOverDebug();
void addBreakpoint(RVA offset); void addBreakpoint(RVA addr);
void delBreakpoint(RVA addr);
void delAllBreakpoints();
void enableBreakpoint(RVA addr);
void disableBreakpoint(RVA addr);
QString getActiveDebugPlugin(); QString getActiveDebugPlugin();
QStringList getDebugPlugins(); QStringList getDebugPlugins();
void setDebugPlugin(QString plugin); void setDebugPlugin(QString plugin);
@ -536,6 +550,7 @@ public:
QList<MemoryMapDescription> getMemoryMap(); QList<MemoryMapDescription> getMemoryMap();
QList<SearchDescription> getAllSearch(QString search_for, QString space); QList<SearchDescription> getAllSearch(QString search_for, QString space);
BlockStatistics getBlockStatistics(unsigned int blocksCount); BlockStatistics getBlockStatistics(unsigned int blocksCount);
QList<BreakpointDescription> getBreakpoints();
QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function, QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function,
const QString &filterType = QString::null); const QString &filterType = QString::null);
@ -565,6 +580,7 @@ signals:
void commentsChanged(); void commentsChanged();
void registersChanged(); void registersChanged();
void instructionChanged(RVA offset); void instructionChanged(RVA offset);
void breakpointsChanged();
void notesChanged(const QString &notes); void notesChanged(const QString &notes);
void projectSaved(const QString &name); void projectSaved(const QString &name);

View File

@ -178,7 +178,8 @@ SOURCES += \
utils/R2Task.cpp \ utils/R2Task.cpp \
widgets/DebugToolbar.cpp \ widgets/DebugToolbar.cpp \
widgets/MemoryMapWidget.cpp \ widgets/MemoryMapWidget.cpp \
dialogs/preferences/DebugOptionsWidget.cpp dialogs/preferences/DebugOptionsWidget.cpp \
widgets/BreakpointWidget.cpp
HEADERS += \ HEADERS += \
Cutter.h \ Cutter.h \
@ -268,7 +269,8 @@ HEADERS += \
utils/R2Task.h \ utils/R2Task.h \
widgets/DebugToolbar.h \ widgets/DebugToolbar.h \
widgets/MemoryMapWidget.h \ widgets/MemoryMapWidget.h \
dialogs/preferences/DebugOptionsWidget.h dialogs/preferences/DebugOptionsWidget.h \
widgets/BreakpointWidget.h
FORMS += \ FORMS += \
dialogs/AboutDialog.ui \ dialogs/AboutDialog.ui \
@ -319,7 +321,8 @@ FORMS += \
dialogs/OpenFileDialog.ui \ dialogs/OpenFileDialog.ui \
widgets/MemoryMapWidget.ui \ widgets/MemoryMapWidget.ui \
widgets/MemoryMapWidget.ui \ widgets/MemoryMapWidget.ui \
dialogs/preferences/DebugOptionsWidget.ui dialogs/preferences/DebugOptionsWidget.ui \
widgets/BreakpointWidget.ui
RESOURCES += \ RESOURCES += \
resources.qrc \ resources.qrc \

View File

@ -75,6 +75,7 @@
#include "widgets/ZignaturesWidget.h" #include "widgets/ZignaturesWidget.h"
#include "widgets/DebugToolbar.h" #include "widgets/DebugToolbar.h"
#include "widgets/MemoryMapWidget.h" #include "widgets/MemoryMapWidget.h"
#include "widgets/BreakpointWidget.h"
// Graphics // Graphics
#include <QGraphicsEllipseItem> #include <QGraphicsEllipseItem>
@ -206,6 +207,7 @@ void MainWindow::initUI()
backtraceDock = new BacktraceWidget(this, ui->actionBacktrace); backtraceDock = new BacktraceWidget(this, ui->actionBacktrace);
registersDock = new RegistersWidget(this, ui->actionRegisters); registersDock = new RegistersWidget(this, ui->actionRegisters);
memoryMapDock = new MemoryMapWidget(this, ui->actionMemoryMap); memoryMapDock = new MemoryMapWidget(this, ui->actionMemoryMap);
breakpointDock = new BreakpointWidget(this, ui->actionBreakpoint);
#ifdef CUTTER_ENABLE_JUPYTER #ifdef CUTTER_ENABLE_JUPYTER
jupyterDock = new JupyterWidget(this, ui->actionJupyter); jupyterDock = new JupyterWidget(this, ui->actionJupyter);
#else #else
@ -545,8 +547,9 @@ void MainWindow::restoreDocks()
addExtraWidget(stackDock); addExtraWidget(stackDock);
splitDockWidget(stackDock, registersDock, Qt::Vertical); splitDockWidget(stackDock, registersDock, Qt::Vertical);
splitDockWidget(stackDock, backtraceDock, Qt::Vertical); splitDockWidget(stackDock, backtraceDock, Qt::Vertical);
// MemoryMap widget goes in the center tabs // MemoryMap/Breakpoint widget goes in the center tabs
tabifyDockWidget(dashboardDock, memoryMapDock); tabifyDockWidget(dashboardDock, memoryMapDock);
tabifyDockWidget(dashboardDock, breakpointDock);
#ifdef CUTTER_ENABLE_JUPYTER #ifdef CUTTER_ENABLE_JUPYTER
tabifyDockWidget(dashboardDock, jupyterDock); tabifyDockWidget(dashboardDock, jupyterDock);
#endif #endif
@ -629,7 +632,8 @@ void MainWindow::showDebugDocks()
stackDock, stackDock,
registersDock, registersDock,
backtraceDock, backtraceDock,
memoryMapDock memoryMapDock,
breakpointDock
}; };
for (auto w : dockWidgets) { for (auto w : dockWidgets) {
if (debugDocks.contains(w)) { if (debugDocks.contains(w)) {

View File

@ -226,6 +226,7 @@ private:
QDockWidget *backtraceDock = nullptr; QDockWidget *backtraceDock = nullptr;
QDockWidget *memoryMapDock = nullptr; QDockWidget *memoryMapDock = nullptr;
NewFileDialog *newFileDialog = nullptr; NewFileDialog *newFileDialog = nullptr;
QDockWidget *breakpointDock = nullptr;
#ifdef CUTTER_ENABLE_JUPYTER #ifdef CUTTER_ENABLE_JUPYTER
JupyterWidget *jupyterDock = nullptr; JupyterWidget *jupyterDock = nullptr;
#endif #endif

View File

@ -231,6 +231,7 @@ border-top: 0px;
<addaction name="actionRegisters"/> <addaction name="actionRegisters"/>
<addaction name="actionBacktrace"/> <addaction name="actionBacktrace"/>
<addaction name="actionMemoryMap"/> <addaction name="actionMemoryMap"/>
<addaction name="actionBreakpoint"/>
</widget> </widget>
<widget class="QMenu" name="addInfoWidgets"> <widget class="QMenu" name="addInfoWidgets">
<property name="title"> <property name="title">
@ -1046,6 +1047,14 @@ border-top: 0px;
<string>Memory map</string> <string>Memory map</string>
</property> </property>
</action> </action>
<action name="actionBreakpoint">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Breakpoints</string>
</property>
</action>
<action name="actionClasses"> <action name="actionClasses">
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>

View File

@ -0,0 +1,197 @@
#include "BreakpointWidget.h"
#include "ui_BreakpointWidget.h"
#include "MainWindow.h"
#include "utils/Helpers.h"
#include <QMenu>
BreakpointModel::BreakpointModel(QList<BreakpointDescription> *breakpoints, QObject *parent)
: QAbstractListModel(parent),
breakpoints(breakpoints)
{
}
int BreakpointModel::rowCount(const QModelIndex &) const
{
return breakpoints->count();
}
int BreakpointModel::columnCount(const QModelIndex &) const
{
return BreakpointModel::ColumnCount;
}
QVariant BreakpointModel::data(const QModelIndex &index, int role) const
{
if (index.row() >= breakpoints->count())
return QVariant();
const BreakpointDescription &breakpoint = breakpoints->at(index.row());
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case AddrColumn:
return RAddressString(breakpoint.addr);
case PermColumn:
return breakpoint.permission;
case HwColumn:
return breakpoint.hw;
case TraceColumn:
return breakpoint.trace;
case EnabledColumn:
return breakpoint.enabled;
default:
return QVariant();
}
case BreakpointDescriptionRole:
return QVariant::fromValue(breakpoint);
default:
return QVariant();
}
}
QVariant BreakpointModel::headerData(int section, Qt::Orientation, int role) const
{
switch (role) {
case Qt::DisplayRole:
switch (section) {
case AddrColumn:
return tr("Offset");
case PermColumn:
return tr("Permissions");
case HwColumn:
return tr("Hardware bp");
case TraceColumn:
return tr("Tracing");
case EnabledColumn:
return tr("Active");
default:
return QVariant();
}
default:
return QVariant();
}
}
void BreakpointModel::beginReloadBreakpoint()
{
beginResetModel();
}
void BreakpointModel::endReloadBreakpoint()
{
endResetModel();
}
BreakpointProxyModel::BreakpointProxyModel(BreakpointModel *sourceModel, QObject *parent)
: QSortFilterProxyModel(parent)
{
setSourceModel(sourceModel);
}
bool BreakpointProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
{
QModelIndex index = sourceModel()->index(row, 0, parent);
BreakpointDescription item = index.data(BreakpointModel::BreakpointDescriptionRole).value<BreakpointDescription>();
return item.permission.contains(filterRegExp());
}
bool BreakpointProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
BreakpointDescription leftBreakpt = left.data(BreakpointModel::BreakpointDescriptionRole).value<BreakpointDescription>();
BreakpointDescription rightBreakpt = right.data(BreakpointModel::BreakpointDescriptionRole).value<BreakpointDescription>();
switch (left.column()) {
case BreakpointModel::AddrColumn:
return leftBreakpt.addr < rightBreakpt.addr;
case BreakpointModel::HwColumn:
return leftBreakpt.hw < rightBreakpt.hw;
case BreakpointModel::PermColumn:
return leftBreakpt.permission < rightBreakpt.permission;
case BreakpointModel::EnabledColumn:
return leftBreakpt.enabled < rightBreakpt.enabled;
default:
break;
}
return leftBreakpt.addr < rightBreakpt.addr;
}
BreakpointWidget::BreakpointWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action),
ui(new Ui::BreakpointWidget)
{
ui->setupUi(this);
breakpointModel = new BreakpointModel(&breakpoints, this);
breakpointProxyModel = new BreakpointProxyModel(breakpointModel, this);
ui->breakpointTreeView->setModel(breakpointProxyModel);
ui->breakpointTreeView->sortByColumn(BreakpointModel::AddrColumn, Qt::AscendingOrder);
setScrollMode();
actionDelBreakpoint = new QAction(tr("Delete breakpoint"));
actionToggleBreakpoint = new QAction(tr("Toggle breakpoint"));
connect(actionDelBreakpoint, &QAction::triggered, this, &BreakpointWidget::delBreakpoint);
connect(actionToggleBreakpoint, &QAction::triggered, this, &BreakpointWidget::toggleBreakpoint);
connect(Core(), &CutterCore::refreshAll, this, &BreakpointWidget::refreshBreakpoint);
connect(Core(), &CutterCore::breakpointsChanged, this, &BreakpointWidget::refreshBreakpoint);
connect(ui->delAllBreakpoints, &QAbstractButton::clicked, Core(), &CutterCore::delAllBreakpoints);
ui->breakpointTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->breakpointTreeView, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(showBreakpointContextMenu(const QPoint &)));
}
BreakpointWidget::~BreakpointWidget() {}
void BreakpointWidget::refreshBreakpoint()
{
breakpointModel->beginReloadBreakpoint();
breakpoints = Core()->getBreakpoints();
breakpointModel->endReloadBreakpoint();
ui->breakpointTreeView->resizeColumnToContents(0);
ui->breakpointTreeView->resizeColumnToContents(1);
ui->breakpointTreeView->resizeColumnToContents(2);
}
void BreakpointWidget::setScrollMode()
{
qhelpers::setVerticalScrollMode(ui->breakpointTreeView);
}
void BreakpointWidget::on_breakpointTreeView_doubleClicked(const QModelIndex &index)
{
BreakpointDescription item = index.data(BreakpointModel::BreakpointDescriptionRole).value<BreakpointDescription>();
Core()->seek(item.addr);
}
void BreakpointWidget::showBreakpointContextMenu(const QPoint &pt)
{
QMenu *menu = new QMenu(ui->breakpointTreeView);
menu->clear();
menu->addAction(actionDelBreakpoint);
menu->addAction(actionToggleBreakpoint);
menu->exec(ui->breakpointTreeView->mapToGlobal(pt));
this->setContextMenuPolicy(Qt::CustomContextMenu);
delete menu;
}
void BreakpointWidget::delBreakpoint()
{
BreakpointDescription bp = ui->breakpointTreeView->selectionModel()->currentIndex().data(
BreakpointModel::BreakpointDescriptionRole).value<BreakpointDescription>();
Core()->delBreakpoint(bp.addr);
}
void BreakpointWidget::toggleBreakpoint()
{
BreakpointDescription bp = ui->breakpointTreeView->selectionModel()->currentIndex().data(
BreakpointModel::BreakpointDescriptionRole).value<BreakpointDescription>();
if (bp.enabled) {
Core()->disableBreakpoint(bp.addr);
} else {
Core()->enableBreakpoint(bp.addr);
}
}

View File

@ -0,0 +1,89 @@
#pragma once
#include <memory>
#include "Cutter.h"
#include "CutterDockWidget.h"
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
class MainWindow;
class QTreeWidget;
namespace Ui
{
class BreakpointWidget;
}
class MainWindow;
class QTreeWidgetItem;
class BreakpointModel: public QAbstractListModel
{
Q_OBJECT
private:
QList<BreakpointDescription> *breakpoints;
public:
enum Column { AddrColumn = 0, PermColumn, HwColumn, TraceColumn, EnabledColumn, ColumnCount };
enum Role { BreakpointDescriptionRole = Qt::UserRole };
BreakpointModel(QList<BreakpointDescription> *breakpoints, QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
void beginReloadBreakpoint();
void endReloadBreakpoint();
};
class BreakpointProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
BreakpointProxyModel(BreakpointModel *sourceModel, QObject *parent = nullptr);
protected:
bool filterAcceptsRow(int row, const QModelIndex &parent) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
};
class BreakpointWidget : public CutterDockWidget
{
Q_OBJECT
public:
explicit BreakpointWidget(MainWindow *main, QAction *action = nullptr);
~BreakpointWidget();
private slots:
void on_breakpointTreeView_doubleClicked(const QModelIndex &index);
void showBreakpointContextMenu(const QPoint &pt);
void delBreakpoint();
void toggleBreakpoint();
void refreshBreakpoint();
private:
std::unique_ptr<Ui::BreakpointWidget> ui;
BreakpointModel *breakpointModel;
BreakpointProxyModel *breakpointProxyModel;
QList<BreakpointDescription> breakpoints;
QAction *actionDelBreakpoint = nullptr;
QAction *actionToggleBreakpoint = nullptr;
void setScrollMode();
};

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BreakpointWidget</class>
<widget class="QDockWidget" name="BreakpointWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Breakpoints</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTreeView" name="breakpointTreeView">
<property name="styleSheet">
<string notr="true">QTreeView::item
{
padding-top: 1px;
padding-bottom: 1px;
}</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="indentation">
<number>8</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="delAllBreakpoints">
<property name="text">
<string>Delete all breakpoints</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>