Debug toolbar (#521)
@ -754,6 +754,47 @@ QString CutterCore::getRegisterName(QString registerRole)
|
||||
void CutterCore::setRegister(QString regName, QString 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()
|
||||
@ -761,6 +802,29 @@ QJsonDocument CutterCore::getBacktrace()
|
||||
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 stats;
|
||||
|
21
src/Cutter.h
@ -294,6 +294,15 @@ struct BlockStatistics {
|
||||
QList<BlockDescription> blocks;
|
||||
};
|
||||
|
||||
struct MemoryMapDescription {
|
||||
RVA addrStart;
|
||||
RVA addrEnd;
|
||||
QString name;
|
||||
QString fileName;
|
||||
QString type;
|
||||
QString permission;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(FunctionDescription)
|
||||
Q_DECLARE_METATYPE(ImportDescription)
|
||||
Q_DECLARE_METATYPE(ExportDescription)
|
||||
@ -322,6 +331,7 @@ Q_DECLARE_METATYPE(HeaderDescription)
|
||||
Q_DECLARE_METATYPE(ZignatureDescription)
|
||||
Q_DECLARE_METATYPE(SearchDescription)
|
||||
Q_DECLARE_METATYPE(SectionDescription)
|
||||
Q_DECLARE_METATYPE(MemoryMapDescription)
|
||||
|
||||
class CutterCore: public QObject
|
||||
{
|
||||
@ -450,12 +460,21 @@ public:
|
||||
ulong get_baddr();
|
||||
QList<QList<QString>> get_exec_sections();
|
||||
QString getOffsetInfo(QString addr);
|
||||
|
||||
// Debug
|
||||
QJsonDocument getRegistersInfo();
|
||||
QJsonDocument getRegisterValues();
|
||||
QString getRegisterName(QString registerRole);
|
||||
void setRegister(QString regName, QString regValue);
|
||||
QJsonDocument getStack(int size = 0x40);
|
||||
QJsonDocument getBacktrace();
|
||||
void startDebug();
|
||||
void continueDebug();
|
||||
void continueUntilDebug(QString offset);
|
||||
void stepDebug();
|
||||
void stepOverDebug();
|
||||
void addBreakpoint(RVA offset);
|
||||
|
||||
RVA getOffsetJump(RVA addr);
|
||||
QString getDecompiledCode(RVA addr);
|
||||
QString getDecompiledCode(QString addr);
|
||||
@ -508,6 +527,7 @@ public:
|
||||
QList<ResourcesDescription> getAllResources();
|
||||
QList<VTableDescription> getAllVTables();
|
||||
QList<TypeDescription> getAllTypes();
|
||||
QList<MemoryMapDescription> getMemoryMap();
|
||||
QList<SearchDescription> getAllSearch(QString search_for, QString space);
|
||||
BlockStatistics getBlockStatistics(unsigned int blocksCount);
|
||||
|
||||
@ -537,6 +557,7 @@ signals:
|
||||
void functionsChanged();
|
||||
void flagsChanged();
|
||||
void commentsChanged();
|
||||
void registersChanged();
|
||||
void instructionChanged(RVA offset);
|
||||
|
||||
void notesChanged(const QString ¬es);
|
||||
|
@ -175,7 +175,9 @@ SOURCES += \
|
||||
dialogs/OpenFileDialog.cpp \
|
||||
utils/CommandTask.cpp \
|
||||
utils/ProgressIndicator.cpp \
|
||||
utils/R2Task.cpp
|
||||
utils/R2Task.cpp \
|
||||
widgets/DebugToolbar.cpp \
|
||||
widgets/MemoryMapWidget.cpp
|
||||
|
||||
HEADERS += \
|
||||
Cutter.h \
|
||||
@ -262,7 +264,9 @@ HEADERS += \
|
||||
utils/CommandTask.h \
|
||||
utils/ProgressIndicator.h \
|
||||
plugins/CutterPlugin.h \
|
||||
utils/R2Task.h
|
||||
utils/R2Task.h \
|
||||
widgets/DebugToolbar.h \
|
||||
widgets/MemoryMapWidget.h
|
||||
|
||||
FORMS += \
|
||||
dialogs/AboutDialog.ui \
|
||||
@ -310,7 +314,8 @@ FORMS += \
|
||||
widgets/StackWidget.ui \
|
||||
widgets/RegistersWidget.ui \
|
||||
widgets/BacktraceWidget.ui \
|
||||
dialogs/OpenFileDialog.ui
|
||||
dialogs/OpenFileDialog.ui \
|
||||
widgets/MemoryMapWidget.ui
|
||||
|
||||
RESOURCES += \
|
||||
resources.qrc \
|
||||
|
@ -73,6 +73,8 @@
|
||||
#include "widgets/JupyterWidget.h"
|
||||
#include "widgets/HeadersWidget.h"
|
||||
#include "widgets/ZignaturesWidget.h"
|
||||
#include "widgets/DebugToolbar.h"
|
||||
#include "widgets/MemoryMapWidget.h"
|
||||
|
||||
// Graphics
|
||||
#include <QGraphicsEllipseItem>
|
||||
@ -124,9 +126,19 @@ void MainWindow::initUI()
|
||||
QWidget *spacer3 = new QWidget();
|
||||
spacer3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
spacer3->setMinimumSize(20, 20);
|
||||
spacer3->setMaximumWidth(300);
|
||||
spacer3->setMaximumWidth(100);
|
||||
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
|
||||
this->omnibar = new Omnibar(this);
|
||||
ui->mainToolBar->addWidget(this->omnibar);
|
||||
@ -193,6 +205,7 @@ void MainWindow::initUI()
|
||||
stackDock = new StackWidget(this, ui->actionStack);
|
||||
backtraceDock = new BacktraceWidget(this, ui->actionBacktrace);
|
||||
registersDock = new RegistersWidget(this, ui->actionRegisters);
|
||||
memoryMapDock = new MemoryMapWidget(this, ui->actionMemoryMap);
|
||||
#ifdef CUTTER_ENABLE_JUPYTER
|
||||
jupyterDock = new JupyterWidget(this, ui->actionJupyter);
|
||||
#else
|
||||
@ -531,6 +544,7 @@ void MainWindow::restoreDocks()
|
||||
addExtraWidget(stackDock);
|
||||
splitDockWidget(stackDock, registersDock, Qt::Vertical);
|
||||
splitDockWidget(stackDock, backtraceDock, Qt::Vertical);
|
||||
splitDockWidget(stackDock, memoryMapDock, Qt::Vertical);
|
||||
#ifdef CUTTER_ENABLE_JUPYTER
|
||||
tabifyDockWidget(dashboardDock, jupyterDock);
|
||||
#endif
|
||||
|
@ -222,6 +222,7 @@ private:
|
||||
QDockWidget *stackDock = nullptr;
|
||||
QDockWidget *registersDock = nullptr;
|
||||
QDockWidget *backtraceDock = nullptr;
|
||||
QDockWidget *memoryMapDock = nullptr;
|
||||
NewFileDialog *newFileDialog = nullptr;
|
||||
#ifdef CUTTER_ENABLE_JUPYTER
|
||||
JupyterWidget *jupyterDock = nullptr;
|
||||
|
@ -230,6 +230,7 @@ border-top: 0px;
|
||||
<addaction name="actionStack"/>
|
||||
<addaction name="actionRegisters"/>
|
||||
<addaction name="actionBacktrace"/>
|
||||
<addaction name="actionMemoryMap"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="addInfoWidgets">
|
||||
<property name="title">
|
||||
@ -294,23 +295,6 @@ border-top: 0px;
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Main toolbar</string>
|
||||
</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">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
@ -457,7 +441,7 @@ background-color: palette(dark);
|
||||
<action name="actionBackward">
|
||||
<property name="icon">
|
||||
<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 name="text">
|
||||
<string>Back</string>
|
||||
@ -469,7 +453,7 @@ background-color: palette(dark);
|
||||
<action name="actionForward">
|
||||
<property name="icon">
|
||||
<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 name="text">
|
||||
<string>Forward</string>
|
||||
@ -1054,6 +1038,14 @@ background-color: palette(dark);
|
||||
<string>Backtrace</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMemoryMap">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Memory map</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionClasses">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
|
4
src/img/icons/arrow_left_light.svg
Normal 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 |
4
src/img/icons/arrow_right_light.svg
Normal 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 |
4
src/img/icons/continue_until_main.svg
Normal 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 |
5
src/img/icons/media-skip-forward_light.svg
Normal 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 |
4
src/img/icons/play_light.svg
Normal 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 |
3
src/img/icons/step_light.svg
Normal 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 |
4
src/img/icons/step_over_light.svg
Normal 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 |
@ -41,7 +41,10 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent)
|
||||
actionSetBaseString(this),
|
||||
actionSetBits16(this),
|
||||
actionSetBits32(this),
|
||||
actionSetBits64(this)
|
||||
actionSetBits64(this),
|
||||
actionContinueUntil(this),
|
||||
actionAddBreakpoint(this),
|
||||
actionSetPC(this)
|
||||
{
|
||||
createAction(&actionCopy, tr("Copy"), getCopySequence(), SLOT(on_actionCopy_triggered()));
|
||||
copySeparator = addSeparator();
|
||||
@ -107,6 +110,17 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent)
|
||||
actionJmpReverse.setText(tr("Reverse Jump"));
|
||||
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,
|
||||
SLOT(on_actionEditInstruction_triggered()));
|
||||
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(&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()));
|
||||
}
|
||||
|
||||
@ -216,6 +237,10 @@ void DisassemblyContextMenu::aboutToShowSlot()
|
||||
// decide to show Reverse jmp option
|
||||
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
|
||||
@ -325,6 +350,22 @@ void DisassemblyContextMenu::on_actionCopyAddr_triggered()
|
||||
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()
|
||||
{
|
||||
QString oldComment = Core()->cmd("CC." + RAddressString(offset));
|
||||
|
@ -55,6 +55,9 @@ private slots:
|
||||
void on_actionSetBits16_triggered();
|
||||
void on_actionSetBits32_triggered();
|
||||
void on_actionSetBits64_triggered();
|
||||
void on_actionAddBreakpoint_triggered();
|
||||
void on_actionContinueUntil_triggered();
|
||||
void on_actionSetPC_triggered();
|
||||
|
||||
private:
|
||||
QKeySequence getCopySequence() const;
|
||||
@ -111,6 +114,12 @@ private:
|
||||
QAction actionSetBits32;
|
||||
QAction actionSetBits64;
|
||||
|
||||
QMenu *debugMenu;
|
||||
QAction *debugMenuAction;
|
||||
QAction actionContinueUntil;
|
||||
QAction actionAddBreakpoint;
|
||||
QAction actionSetPC;
|
||||
|
||||
// For creating anonymous entries (that are always visible)
|
||||
void createAction(QString name, QKeySequence keySequence, const char *slot);
|
||||
void createAction(QAction *action, QString name, QKeySequence keySequence, const char *slot);
|
||||
|
@ -1,7 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>img/icons/arrow_left.svg</file>
|
||||
<file>img/icons/arrow_right.svg</file>
|
||||
<file>img/icons/arrow_left_light.svg</file>
|
||||
<file>img/icons/arrow_right_light.svg</file>
|
||||
<file>img/icons/sidebar.svg</file>
|
||||
<file>img/icons/undo.svg</file>
|
||||
<file>img/icons/redo.svg</file>
|
||||
@ -17,6 +17,11 @@
|
||||
<file>img/icons/spin.svg</file>
|
||||
<file>img/icons/plus.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/down.svg</file>
|
||||
<file>img/icons/down_white.svg</file>
|
||||
|
39
src/widgets/DebugToolbar.cpp
Normal 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"));
|
||||
}
|
21
src/widgets/DebugToolbar.h
Normal 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();
|
||||
|
||||
};
|
154
src/widgets/MemoryMapWidget.cpp
Normal 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);
|
||||
}
|
84
src/widgets/MemoryMapWidget.h
Normal 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();
|
||||
};
|
58
src/widgets/MemoryMapWidget.ui
Normal 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>
|
@ -19,7 +19,7 @@ RegistersWidget::RegistersWidget(MainWindow *main, QAction *action) :
|
||||
|
||||
ui->verticalLayout->addWidget(buttonSetRegisters);
|
||||
connect(Core(), &CutterCore::refreshAll, this, &RegistersWidget::updateContents);
|
||||
connect(Core(), &CutterCore::seekChanged, this, &RegistersWidget::updateContents);
|
||||
connect(Core(), &CutterCore::registersChanged, this, &RegistersWidget::updateContents);
|
||||
}
|
||||
|
||||
RegistersWidget::~RegistersWidget() {}
|
||||
|