Debug toolbar (#521)

This commit is contained in:
fcasal 2018-06-12 09:43:14 +01:00 committed by xarkes
parent b74560eb0f
commit 7eea0ed311
22 changed files with 563 additions and 27 deletions

View File

@ -754,6 +754,47 @@ QString CutterCore::getRegisterName(QString registerRole)
void CutterCore::setRegister(QString regName, QString regValue) void CutterCore::setRegister(QString regName, QString regValue)
{ {
cmd("dr " + regName + "=" + regValue); cmd("dr " + regName + "=" + regValue);
emit registersChanged();
}
void CutterCore::startDebug()
{
cmd("ood");
emit registersChanged();
}
void CutterCore::continueDebug()
{
cmd("dc");
emit registersChanged();
}
void CutterCore::continueUntilDebug(QString offset)
{
cmd("dcu " + offset);
emit registersChanged();
}
void CutterCore::stepDebug()
{
cmd("ds");
QString programCounterValue = cmd("dr?`drn pc`").trimmed();
seek(programCounterValue);
emit registersChanged();
}
void CutterCore::stepOverDebug()
{
cmd("dso");
QString programCounterValue = cmd("dr?`drn pc`").trimmed();
seek(programCounterValue);
emit registersChanged();
}
void CutterCore::addBreakpoint(RVA addr)
{
cmd("db " + RAddressString(addr));
emit instructionChanged(addr);
} }
QJsonDocument CutterCore::getBacktrace() QJsonDocument CutterCore::getBacktrace()
@ -761,6 +802,29 @@ QJsonDocument CutterCore::getBacktrace()
return cmdj("dbtj"); return cmdj("dbtj");
} }
QList<MemoryMapDescription> CutterCore::getMemoryMap()
{
QList<MemoryMapDescription> ret;
QJsonArray memoryMapArray = cmdj("dmj").array();
for (QJsonValue value : memoryMapArray) {
QJsonObject memMapObject = value.toObject();
MemoryMapDescription memMap;
memMap.name = memMapObject["name"].toString();
memMap.fileName = memMapObject["file"].toString();
memMap.addrStart = memMapObject["addr"].toVariant().toULongLong();
memMap.addrEnd = memMapObject["addr_end"].toVariant().toULongLong();
memMap.type = memMapObject["type"].toString();
memMap.permission = memMapObject["perm"].toString();
ret << memMap;
}
return ret;
}
QStringList CutterCore::getStats() QStringList CutterCore::getStats()
{ {
QStringList stats; QStringList stats;

View File

@ -294,6 +294,15 @@ struct BlockStatistics {
QList<BlockDescription> blocks; QList<BlockDescription> blocks;
}; };
struct MemoryMapDescription {
RVA addrStart;
RVA addrEnd;
QString name;
QString fileName;
QString type;
QString permission;
};
Q_DECLARE_METATYPE(FunctionDescription) Q_DECLARE_METATYPE(FunctionDescription)
Q_DECLARE_METATYPE(ImportDescription) Q_DECLARE_METATYPE(ImportDescription)
Q_DECLARE_METATYPE(ExportDescription) Q_DECLARE_METATYPE(ExportDescription)
@ -322,6 +331,7 @@ Q_DECLARE_METATYPE(HeaderDescription)
Q_DECLARE_METATYPE(ZignatureDescription) Q_DECLARE_METATYPE(ZignatureDescription)
Q_DECLARE_METATYPE(SearchDescription) Q_DECLARE_METATYPE(SearchDescription)
Q_DECLARE_METATYPE(SectionDescription) Q_DECLARE_METATYPE(SectionDescription)
Q_DECLARE_METATYPE(MemoryMapDescription)
class CutterCore: public QObject class CutterCore: public QObject
{ {
@ -450,12 +460,21 @@ public:
ulong get_baddr(); ulong get_baddr();
QList<QList<QString>> get_exec_sections(); QList<QList<QString>> get_exec_sections();
QString getOffsetInfo(QString addr); QString getOffsetInfo(QString addr);
// Debug
QJsonDocument getRegistersInfo(); QJsonDocument getRegistersInfo();
QJsonDocument getRegisterValues(); QJsonDocument getRegisterValues();
QString getRegisterName(QString registerRole); QString getRegisterName(QString registerRole);
void setRegister(QString regName, QString regValue); void setRegister(QString regName, QString regValue);
QJsonDocument getStack(int size = 0x40); QJsonDocument getStack(int size = 0x40);
QJsonDocument getBacktrace(); QJsonDocument getBacktrace();
void startDebug();
void continueDebug();
void continueUntilDebug(QString offset);
void stepDebug();
void stepOverDebug();
void addBreakpoint(RVA offset);
RVA getOffsetJump(RVA addr); RVA getOffsetJump(RVA addr);
QString getDecompiledCode(RVA addr); QString getDecompiledCode(RVA addr);
QString getDecompiledCode(QString addr); QString getDecompiledCode(QString addr);
@ -508,6 +527,7 @@ public:
QList<ResourcesDescription> getAllResources(); QList<ResourcesDescription> getAllResources();
QList<VTableDescription> getAllVTables(); QList<VTableDescription> getAllVTables();
QList<TypeDescription> getAllTypes(); QList<TypeDescription> getAllTypes();
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);
@ -537,6 +557,7 @@ signals:
void functionsChanged(); void functionsChanged();
void flagsChanged(); void flagsChanged();
void commentsChanged(); void commentsChanged();
void registersChanged();
void instructionChanged(RVA offset); void instructionChanged(RVA offset);
void notesChanged(const QString &notes); void notesChanged(const QString &notes);

View File

@ -175,7 +175,9 @@ SOURCES += \
dialogs/OpenFileDialog.cpp \ dialogs/OpenFileDialog.cpp \
utils/CommandTask.cpp \ utils/CommandTask.cpp \
utils/ProgressIndicator.cpp \ utils/ProgressIndicator.cpp \
utils/R2Task.cpp utils/R2Task.cpp \
widgets/DebugToolbar.cpp \
widgets/MemoryMapWidget.cpp
HEADERS += \ HEADERS += \
Cutter.h \ Cutter.h \
@ -262,7 +264,9 @@ HEADERS += \
utils/CommandTask.h \ utils/CommandTask.h \
utils/ProgressIndicator.h \ utils/ProgressIndicator.h \
plugins/CutterPlugin.h \ plugins/CutterPlugin.h \
utils/R2Task.h utils/R2Task.h \
widgets/DebugToolbar.h \
widgets/MemoryMapWidget.h
FORMS += \ FORMS += \
dialogs/AboutDialog.ui \ dialogs/AboutDialog.ui \
@ -310,7 +314,8 @@ FORMS += \
widgets/StackWidget.ui \ widgets/StackWidget.ui \
widgets/RegistersWidget.ui \ widgets/RegistersWidget.ui \
widgets/BacktraceWidget.ui \ widgets/BacktraceWidget.ui \
dialogs/OpenFileDialog.ui dialogs/OpenFileDialog.ui \
widgets/MemoryMapWidget.ui
RESOURCES += \ RESOURCES += \
resources.qrc \ resources.qrc \

View File

@ -73,6 +73,8 @@
#include "widgets/JupyterWidget.h" #include "widgets/JupyterWidget.h"
#include "widgets/HeadersWidget.h" #include "widgets/HeadersWidget.h"
#include "widgets/ZignaturesWidget.h" #include "widgets/ZignaturesWidget.h"
#include "widgets/DebugToolbar.h"
#include "widgets/MemoryMapWidget.h"
// Graphics // Graphics
#include <QGraphicsEllipseItem> #include <QGraphicsEllipseItem>
@ -124,9 +126,19 @@ void MainWindow::initUI()
QWidget *spacer3 = new QWidget(); QWidget *spacer3 = new QWidget();
spacer3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); spacer3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
spacer3->setMinimumSize(20, 20); spacer3->setMinimumSize(20, 20);
spacer3->setMaximumWidth(300); spacer3->setMaximumWidth(100);
ui->mainToolBar->addWidget(spacer3); ui->mainToolBar->addWidget(spacer3);
QToolBar *debugToolbar = new DebugToolbar(this);
ui->mainToolBar->addWidget(debugToolbar);
// Sepparator between undo/redo and goto lineEdit
QWidget *spacer4 = new QWidget();
spacer4->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
spacer4->setMinimumSize(20, 20);
spacer4->setMaximumWidth(100);
ui->mainToolBar->addWidget(spacer4);
// Omnibar LineEdit // Omnibar LineEdit
this->omnibar = new Omnibar(this); this->omnibar = new Omnibar(this);
ui->mainToolBar->addWidget(this->omnibar); ui->mainToolBar->addWidget(this->omnibar);
@ -193,6 +205,7 @@ void MainWindow::initUI()
stackDock = new StackWidget(this, ui->actionStack); stackDock = new StackWidget(this, ui->actionStack);
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);
#ifdef CUTTER_ENABLE_JUPYTER #ifdef CUTTER_ENABLE_JUPYTER
jupyterDock = new JupyterWidget(this, ui->actionJupyter); jupyterDock = new JupyterWidget(this, ui->actionJupyter);
#else #else
@ -531,6 +544,7 @@ 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);
splitDockWidget(stackDock, memoryMapDock, Qt::Vertical);
#ifdef CUTTER_ENABLE_JUPYTER #ifdef CUTTER_ENABLE_JUPYTER
tabifyDockWidget(dashboardDock, jupyterDock); tabifyDockWidget(dashboardDock, jupyterDock);
#endif #endif

View File

@ -222,6 +222,7 @@ private:
QDockWidget *stackDock = nullptr; QDockWidget *stackDock = nullptr;
QDockWidget *registersDock = nullptr; QDockWidget *registersDock = nullptr;
QDockWidget *backtraceDock = nullptr; QDockWidget *backtraceDock = nullptr;
QDockWidget *memoryMapDock = nullptr;
NewFileDialog *newFileDialog = nullptr; NewFileDialog *newFileDialog = nullptr;
#ifdef CUTTER_ENABLE_JUPYTER #ifdef CUTTER_ENABLE_JUPYTER
JupyterWidget *jupyterDock = nullptr; JupyterWidget *jupyterDock = nullptr;

View File

@ -230,6 +230,7 @@ border-top: 0px;
<addaction name="actionStack"/> <addaction name="actionStack"/>
<addaction name="actionRegisters"/> <addaction name="actionRegisters"/>
<addaction name="actionBacktrace"/> <addaction name="actionBacktrace"/>
<addaction name="actionMemoryMap"/>
</widget> </widget>
<widget class="QMenu" name="addInfoWidgets"> <widget class="QMenu" name="addInfoWidgets">
<property name="title"> <property name="title">
@ -294,23 +295,6 @@ border-top: 0px;
<property name="windowTitle"> <property name="windowTitle">
<string notr="true">Main toolbar</string> <string notr="true">Main toolbar</string>
</property> </property>
<property name="styleSheet">
<string notr="true">
QToolButton {
margin: 1px;
margin-left: 2px;
margin-right: 2px;
padding: 2px;
padding-left: 7px;
padding-right: 7px;
border-radius: 6px;
background-color: #ffffff;
}
QToolButton:pressed {
background-color: palette(dark);
}
</string>
</property>
<property name="movable"> <property name="movable">
<bool>false</bool> <bool>false</bool>
</property> </property>
@ -457,7 +441,7 @@ background-color: palette(dark);
<action name="actionBackward"> <action name="actionBackward">
<property name="icon"> <property name="icon">
<iconset resource="resources.qrc"> <iconset resource="resources.qrc">
<normaloff>:/img/icons/arrow_left.svg</normaloff>:/img/icons/arrow_left.svg</iconset> <normaloff>:/img/icons/arrow_left_light.svg</normaloff>:/img/icons/arrow_left_light.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>Back</string> <string>Back</string>
@ -469,7 +453,7 @@ background-color: palette(dark);
<action name="actionForward"> <action name="actionForward">
<property name="icon"> <property name="icon">
<iconset resource="resources.qrc"> <iconset resource="resources.qrc">
<normaloff>:/img/icons/arrow_right.svg</normaloff>:/img/icons/arrow_right.svg</iconset> <normaloff>:/img/icons/arrow_right_light.svg</normaloff>:/img/icons/arrow_right_light.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>Forward</string> <string>Forward</string>
@ -1054,6 +1038,14 @@ background-color: palette(dark);
<string>Backtrace</string> <string>Backtrace</string>
</property> </property>
</action> </action>
<action name="actionMemoryMap">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Memory map</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,4 @@
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
<svg style="enable-background:new 0 0 32 32" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="32px" width="32px" version="1.1" y="0px" x="0px" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32">
<path d="m16 32l5.7-5.7-6.3-6.3h16v-8h-17l6.3-6.3-5-5.7-16 16 16 16z" fill="#aaacaf"/>
</svg>

After

Width:  |  Height:  |  Size: 418 B

View File

@ -0,0 +1,4 @@
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
<svg style="enable-background:new 0 0 32 32" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="32px" width="32px" version="1.1" y="0px" x="0px" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32">
<path d="m16 0l-5.7 5.7 6.3 6.3h-16v8h17l-6.3 6.3 5 6 16-16-16-16z" fill="#aaacaf"/>
</svg>

After

Width:  |  Height:  |  Size: 416 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M0 0v6l4-3-4-3z" transform="translate(0 1)" fill="#aaacaf"/>
<text x="4" y="4" font-family="Helvetica, Arial, sans-serif" font-size="5" stroke-width="1" stroke="#aaacaf" fill="#aaacaf" >M</text>
</svg>

After

Width:  |  Height:  |  Size: 295 B

View File

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<g fill="#aaacaf">
<path d="M0 0v6l4-3-4-3zm4 3v3l4-3-4-3v3z" transform="translate(0 1)" fill="#aaacaf"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 205 B

View File

@ -0,0 +1,4 @@
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
<svg style="enable-background:new 0 0 24 32" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="32px" width="24px" version="1.1" y="0px" x="0px" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 32">
<polygon points="0 0 24 16 0 32" fill="#aaacaf"/>
</svg>

After

Width:  |  Height:  |  Size: 381 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M0 0v6l5-3-5-3zm5 3v3h2v-6h-2v3z" transform="translate(0 1)" fill="#aaacaf"/>
</svg>

After

Width:  |  Height:  |  Size: 175 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M0 0v6l5-3-5-3zm5 3v3h2v-6h-2v3z" transform="translate(0 1)" fill="#aaacaf"/>
<circle cx="3.5" cy="6.8" r="0.5" stroke="#aaacaf" stroke-width="1" fill="#aaacaf" />
</svg>

After

Width:  |  Height:  |  Size: 263 B

View File

@ -41,7 +41,10 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent)
actionSetBaseString(this), actionSetBaseString(this),
actionSetBits16(this), actionSetBits16(this),
actionSetBits32(this), actionSetBits32(this),
actionSetBits64(this) actionSetBits64(this),
actionContinueUntil(this),
actionAddBreakpoint(this),
actionSetPC(this)
{ {
createAction(&actionCopy, tr("Copy"), getCopySequence(), SLOT(on_actionCopy_triggered())); createAction(&actionCopy, tr("Copy"), getCopySequence(), SLOT(on_actionCopy_triggered()));
copySeparator = addSeparator(); copySeparator = addSeparator();
@ -107,6 +110,17 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent)
actionJmpReverse.setText(tr("Reverse Jump")); actionJmpReverse.setText(tr("Reverse Jump"));
editMenu->addAction(&actionJmpReverse); editMenu->addAction(&actionJmpReverse);
addSeparator();
debugMenu = new QMenu(tr("Debug"), this);
debugMenuAction = addMenu(debugMenu);
actionAddBreakpoint.setText(tr("Add breakpoint"));
debugMenu->addAction(&actionAddBreakpoint);
actionContinueUntil.setText(tr("Continue until line"));
debugMenu->addAction(&actionContinueUntil);
QString progCounterName = Core()->getRegisterName("PC");
actionSetPC.setText("Set " + progCounterName + " here");
debugMenu->addAction(&actionSetPC);
connect(&actionEditInstruction, SIGNAL(triggered(bool)), this, connect(&actionEditInstruction, SIGNAL(triggered(bool)), this,
SLOT(on_actionEditInstruction_triggered())); SLOT(on_actionEditInstruction_triggered()));
connect(&actionNopInstruction, SIGNAL(triggered(bool)), this, connect(&actionNopInstruction, SIGNAL(triggered(bool)), this,
@ -134,6 +148,13 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent)
connect(&actionSetBits32, SIGNAL(triggered(bool)), this, SLOT(on_actionSetBits32_triggered())); connect(&actionSetBits32, SIGNAL(triggered(bool)), this, SLOT(on_actionSetBits32_triggered()));
connect(&actionSetBits64, SIGNAL(triggered(bool)), this, SLOT(on_actionSetBits64_triggered())); connect(&actionSetBits64, SIGNAL(triggered(bool)), this, SLOT(on_actionSetBits64_triggered()));
connect(&actionAddBreakpoint, &QAction::triggered,
this, &DisassemblyContextMenu::on_actionAddBreakpoint_triggered);
connect(&actionContinueUntil, &QAction::triggered,
this, &DisassemblyContextMenu::on_actionContinueUntil_triggered);
connect(&actionSetPC, &QAction::triggered,
this, &DisassemblyContextMenu::on_actionSetPC_triggered);
connect(this, SIGNAL(aboutToShow()), this, SLOT(aboutToShowSlot())); connect(this, SIGNAL(aboutToShow()), this, SLOT(aboutToShowSlot()));
} }
@ -216,6 +237,10 @@ void DisassemblyContextMenu::aboutToShowSlot()
// decide to show Reverse jmp option // decide to show Reverse jmp option
showReverseJmpQuery(); showReverseJmpQuery();
// show debug options
// @TODO determine if we are being debugged and only show the menu in those cases
// maybe using dpt command
debugMenuAction->setVisible(true);
} }
QKeySequence DisassemblyContextMenu::getCopySequence() const QKeySequence DisassemblyContextMenu::getCopySequence() const
@ -325,6 +350,22 @@ void DisassemblyContextMenu::on_actionCopyAddr_triggered()
clipboard->setText(RAddressString(offset)); clipboard->setText(RAddressString(offset));
} }
void DisassemblyContextMenu::on_actionAddBreakpoint_triggered()
{
Core()->addBreakpoint(offset);
}
void DisassemblyContextMenu::on_actionContinueUntil_triggered()
{
Core()->continueUntilDebug(RAddressString(offset));
}
void DisassemblyContextMenu::on_actionSetPC_triggered()
{
QString progCounterName = Core()->getRegisterName("PC");
Core()->setRegister(progCounterName, RAddressString(offset));
}
void DisassemblyContextMenu::on_actionAddComment_triggered() void DisassemblyContextMenu::on_actionAddComment_triggered()
{ {
QString oldComment = Core()->cmd("CC." + RAddressString(offset)); QString oldComment = Core()->cmd("CC." + RAddressString(offset));

View File

@ -55,6 +55,9 @@ private slots:
void on_actionSetBits16_triggered(); void on_actionSetBits16_triggered();
void on_actionSetBits32_triggered(); void on_actionSetBits32_triggered();
void on_actionSetBits64_triggered(); void on_actionSetBits64_triggered();
void on_actionAddBreakpoint_triggered();
void on_actionContinueUntil_triggered();
void on_actionSetPC_triggered();
private: private:
QKeySequence getCopySequence() const; QKeySequence getCopySequence() const;
@ -111,6 +114,12 @@ private:
QAction actionSetBits32; QAction actionSetBits32;
QAction actionSetBits64; QAction actionSetBits64;
QMenu *debugMenu;
QAction *debugMenuAction;
QAction actionContinueUntil;
QAction actionAddBreakpoint;
QAction actionSetPC;
// For creating anonymous entries (that are always visible) // For creating anonymous entries (that are always visible)
void createAction(QString name, QKeySequence keySequence, const char *slot); void createAction(QString name, QKeySequence keySequence, const char *slot);
void createAction(QAction *action, QString name, QKeySequence keySequence, const char *slot); void createAction(QAction *action, QString name, QKeySequence keySequence, const char *slot);

View File

@ -1,7 +1,7 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>img/icons/arrow_left.svg</file> <file>img/icons/arrow_left_light.svg</file>
<file>img/icons/arrow_right.svg</file> <file>img/icons/arrow_right_light.svg</file>
<file>img/icons/sidebar.svg</file> <file>img/icons/sidebar.svg</file>
<file>img/icons/undo.svg</file> <file>img/icons/undo.svg</file>
<file>img/icons/redo.svg</file> <file>img/icons/redo.svg</file>
@ -17,6 +17,11 @@
<file>img/icons/spin.svg</file> <file>img/icons/spin.svg</file>
<file>img/icons/plus.svg</file> <file>img/icons/plus.svg</file>
<file>img/icons/play.svg</file> <file>img/icons/play.svg</file>
<file>img/icons/play_light.svg</file>
<file>img/icons/media-skip-forward_light.svg</file>
<file>img/icons/continue_until_main.svg</file>
<file>img/icons/step_light.svg</file>
<file>img/icons/step_over_light.svg</file>
<file>img/icons/cloud.svg</file> <file>img/icons/cloud.svg</file>
<file>img/icons/down.svg</file> <file>img/icons/down.svg</file>
<file>img/icons/down_white.svg</file> <file>img/icons/down_white.svg</file>

View File

@ -0,0 +1,39 @@
#include "DebugToolbar.h"
#include "MainWindow.h"
#include <QAction>
#include <QPainter>
DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) :
QToolBar(parent),
main(main)
{
setObjectName("debugToolbar");
QIcon startIcon = QIcon(":/img/icons/play_light.svg");
QIcon continueIcon = QIcon(":/img/icons/media-skip-forward_light.svg");
QIcon continueUntilIcon = QIcon(":/img/icons/continue_until_main.svg");
QIcon stepIcon = QIcon(":/img/icons/step_light.svg");
QIcon stepOverIcon = QIcon(":/img/icons/step_over_light.svg");
QAction *actionStart = new QAction(startIcon, tr("Start debug"), parent);
QAction *actionContinue = new QAction(continueIcon, tr("Continue"), parent);
QAction *actionContinueUntilMain = new QAction(continueUntilIcon, tr("Continue until main"), parent);
QAction *actionStep = new QAction(stepIcon, tr("Step"), parent);
QAction *actionStepOver = new QAction(stepOverIcon, tr("Step over"), parent);
addAction(actionStart);
addAction(actionContinue);
addAction(actionContinueUntilMain);
addAction(actionStep);
addAction(actionStepOver);
connect(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug);
connect(actionStart, &QAction::triggered, Core(), &CutterCore::startDebug);
connect(actionStepOver, &QAction::triggered, Core(), &CutterCore::stepOverDebug);
connect(actionContinue, &QAction::triggered, Core(), &CutterCore::continueDebug);
connect(actionContinueUntilMain, &QAction::triggered, this, &DebugToolbar::continueUntilMain);
}
void DebugToolbar::continueUntilMain()
{
Core()->continueUntilDebug(tr("main"));
}

View File

@ -0,0 +1,21 @@
#pragma once
#include <QToolBar>
#include "Cutter.h"
class MainWindow;
class DebugToolbar : public QToolBar
{
Q_OBJECT
public:
explicit DebugToolbar(MainWindow *main, QWidget *parent = nullptr);
private:
MainWindow *main;
private slots:
void continueUntilMain();
};

View File

@ -0,0 +1,154 @@
#include "MemoryMapWidget.h"
#include "ui_MemoryMapWidget.h"
#include "MainWindow.h"
#include "utils/Helpers.h"
MemoryMapModel::MemoryMapModel(QList<MemoryMapDescription> *memoryMaps, QObject *parent)
: QAbstractListModel(parent),
memoryMaps(memoryMaps)
{
}
int MemoryMapModel::rowCount(const QModelIndex &) const
{
return memoryMaps->count();
}
int MemoryMapModel::columnCount(const QModelIndex &) const
{
return MemoryMapModel::ColumnCount;
}
QVariant MemoryMapModel::data(const QModelIndex &index, int role) const
{
if (index.row() >= memoryMaps->count())
return QVariant();
const MemoryMapDescription &memoryMap = memoryMaps->at(index.row());
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case AddrStartColumn:
return RAddressString(memoryMap.addrStart);
case AddrEndColumn:
return RAddressString(memoryMap.addrEnd);
case NameColumn:
return memoryMap.name;
case PermColumn:
return memoryMap.permission;
default:
return QVariant();
}
case MemoryDescriptionRole:
return QVariant::fromValue(memoryMap);
default:
return QVariant();
}
}
QVariant MemoryMapModel::headerData(int section, Qt::Orientation, int role) const
{
switch (role) {
case Qt::DisplayRole:
switch (section) {
case AddrStartColumn:
return tr("Offset start");
case AddrEndColumn:
return tr("Offset end");
case NameColumn:
return tr("Name");
case PermColumn:
return tr("Permissions");
default:
return QVariant();
}
default:
return QVariant();
}
}
void MemoryMapModel::beginReloadMemoryMap()
{
beginResetModel();
}
void MemoryMapModel::endReloadMemoryMap()
{
endResetModel();
}
MemoryProxyModel::MemoryProxyModel(MemoryMapModel *sourceModel, QObject *parent)
: QSortFilterProxyModel(parent)
{
setSourceModel(sourceModel);
}
bool MemoryProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
{
QModelIndex index = sourceModel()->index(row, 0, parent);
MemoryMapDescription item = index.data(MemoryMapModel::MemoryDescriptionRole).value<MemoryMapDescription>();
return item.name.contains(filterRegExp());
}
bool MemoryProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
MemoryMapDescription leftMemMap = left.data(MemoryMapModel::MemoryDescriptionRole).value<MemoryMapDescription>();
MemoryMapDescription rightMemMap = right.data(MemoryMapModel::MemoryDescriptionRole).value<MemoryMapDescription>();
switch (left.column()) {
case MemoryMapModel::AddrStartColumn:
return leftMemMap.addrStart < rightMemMap.addrStart;
case MemoryMapModel::AddrEndColumn:
return leftMemMap.addrEnd < rightMemMap.addrEnd;
case MemoryMapModel::NameColumn:
return leftMemMap.name < rightMemMap.name;
case MemoryMapModel::PermColumn:
return leftMemMap.permission < rightMemMap.permission;
default:
break;
}
return leftMemMap.addrStart < rightMemMap.addrStart;
}
MemoryMapWidget::MemoryMapWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action),
ui(new Ui::MemoryMapWidget)
{
ui->setupUi(this);
memoryModel = new MemoryMapModel(&memoryMaps, this);
memoryProxyModel = new MemoryProxyModel(memoryModel, this);
ui->memoryTreeView->setModel(memoryProxyModel);
ui->memoryTreeView->sortByColumn(MemoryMapModel::AddrStartColumn, Qt::AscendingOrder);
setScrollMode();
connect(Core(), &CutterCore::refreshAll, this, &MemoryMapWidget::refreshMemoryMap);
connect(Core(), &CutterCore::registersChanged, this, &MemoryMapWidget::refreshMemoryMap);
}
MemoryMapWidget::~MemoryMapWidget() {}
void MemoryMapWidget::refreshMemoryMap()
{
memoryModel->beginReloadMemoryMap();
memoryMaps = Core()->getMemoryMap();
memoryModel->endReloadMemoryMap();
ui->memoryTreeView->resizeColumnToContents(0);
ui->memoryTreeView->resizeColumnToContents(1);
ui->memoryTreeView->resizeColumnToContents(2);
}
void MemoryMapWidget::setScrollMode()
{
qhelpers::setVerticalScrollMode(ui->memoryTreeView);
}
void MemoryMapWidget::on_memoryTreeView_doubleClicked(const QModelIndex &index)
{
MemoryMapDescription item = index.data(MemoryMapModel::MemoryDescriptionRole).value<MemoryMapDescription>();
Core()->seek(item.addrStart);
}

View File

@ -0,0 +1,84 @@
#pragma once
#include <memory>
#include "Cutter.h"
#include "CutterDockWidget.h"
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
class MainWindow;
class QTreeWidget;
namespace Ui
{
class MemoryMapWidget;
}
class MainWindow;
class QTreeWidgetItem;
class MemoryMapModel: public QAbstractListModel
{
Q_OBJECT
private:
QList<MemoryMapDescription> *memoryMaps;
public:
enum Column { AddrStartColumn = 0, AddrEndColumn, NameColumn, PermColumn, ColumnCount };
enum Role { MemoryDescriptionRole = Qt::UserRole };
MemoryMapModel(QList<MemoryMapDescription> *memoryMaps, 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 beginReloadMemoryMap();
void endReloadMemoryMap();
};
class MemoryProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
MemoryProxyModel(MemoryMapModel *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 MemoryMapWidget : public CutterDockWidget
{
Q_OBJECT
public:
explicit MemoryMapWidget(MainWindow *main, QAction *action = nullptr);
~MemoryMapWidget();
private slots:
void on_memoryTreeView_doubleClicked(const QModelIndex &index);
void refreshMemoryMap();
private:
std::unique_ptr<Ui::MemoryMapWidget> ui;
MemoryMapModel *memoryModel;
MemoryProxyModel *memoryProxyModel;
QList<MemoryMapDescription> memoryMaps;
void setScrollMode();
};

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MemoryMapWidget</class>
<widget class="QDockWidget" name="MemoryMapWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Memory Map</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="memoryTreeView">
<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>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -19,7 +19,7 @@ RegistersWidget::RegistersWidget(MainWindow *main, QAction *action) :
ui->verticalLayout->addWidget(buttonSetRegisters); ui->verticalLayout->addWidget(buttonSetRegisters);
connect(Core(), &CutterCore::refreshAll, this, &RegistersWidget::updateContents); connect(Core(), &CutterCore::refreshAll, this, &RegistersWidget::updateContents);
connect(Core(), &CutterCore::seekChanged, this, &RegistersWidget::updateContents); connect(Core(), &CutterCore::registersChanged, this, &RegistersWidget::updateContents);
} }
RegistersWidget::~RegistersWidget() {} RegistersWidget::~RegistersWidget() {}