Debugging shortcuts (#578)

* Added debug shortcuts and debug menu
* Added "Add breakpoint" shortcut
* Added debug shortcuts to readme
* Fix double memorymap ui
* Add bp F2 shortcut and fix toggling bp with shortcuts
This commit is contained in:
fcasal 2018-07-17 08:26:20 +01:00 committed by xarkes
parent 41cfb78d13
commit 9f7b96281d
14 changed files with 145 additions and 79 deletions

3
.gitignore vendored
View File

@ -66,3 +66,6 @@ __pycache__
# Other # Other
compile_commands.json compile_commands.json
# vscode
**/.vscode

View File

@ -72,31 +72,44 @@ Check this [page](https://github.com/radareorg/cutter/blob/master/docs/Common-er
To deploy *cutter* using a pre-built `Dockerfile`, it's possible to use the [provided configuration](docker). The corresponding `README.md` file also contains instructions on how to get started using the docker image with minimal effort. To deploy *cutter* using a pre-built `Dockerfile`, it's possible to use the [provided configuration](docker). The corresponding `README.md` file also contains instructions on how to get started using the docker image with minimal effort.
## Keyboard shortcuts ### Global shortcuts
| Shortcut | Function |
| ---------- | ------------------- |
| . | Focus console input |
| G/S | Focus search bar |
| Ctrl/Cmd+R | Refresh contents |
| Shortcut | Function | ### Disassembly view shortcuts
| --- | --- | | Shortcut | Function |
| Global shortcuts: || | ---------- | -------------------------------- |
| . | Focus console input | | Esc | Seek to previous position |
| G & S | Focus search bar | | Space | Switch to disassembly graph view |
| F5 | Refresh contents | | Ctrl/Cmd+C | Copy |
| Disassembly view: || | ; | Add comment |
| Esc | Seek to previous position | | N | Rename current function/flag |
| Space | Switch to disassembly graph view | | Shift+N | Rename flag/function used here |
| Ctrl/Cmd+C | Copy | | X | Show Xrefs |
| ; | Add comment |
| N | Rename current function/flag |
| Shift+N | Rename flag/function used here |
| X | Show Xrefs |
| Disassembly graph view: ||
| Esc | Seek to previous position |
| Space | Switch to disassembly view |
| + | Zoom in |
| - | Zoom out |
| = | Reset zoom |
| J | Next instruction |
| K | Previous instruction |
### Graph view shortcuts
| Shortcut | Function |
| ------------------- | -------------------------- |
| Esc | Seek to previous position |
| Space | Switch to disassembly view |
| Ctrl/Cmd+MouseWheel | Zoom |
| + | Zoom in |
| - | Zoom out |
| = | Reset zoom |
| J | Next instruction |
| K | Previous instruction |
### Debug shortcuts
| Shortcut | Function |
| --------------- | -------------- |
| F9 | Start debug |
| F7 | Step into |
| F8 | Step over |
| F5 | Continue |
| F2/(Ctrl/Cmd)+B | Add breakpoint |
## Help ## Help

View File

@ -933,16 +933,16 @@ void CutterCore::setDebugPlugin(QString plugin)
setConfig("dbg.backend", plugin); setConfig("dbg.backend", plugin);
} }
void CutterCore::addBreakpoint(RVA addr) void CutterCore::toggleBreakpoint(RVA addr)
{ {
cmd("db " + RAddressString(addr)); cmd("dbs " + RAddressString(addr));
emit instructionChanged(addr); emit instructionChanged(addr);
emit breakpointsChanged(); emit breakpointsChanged();
} }
void CutterCore::addBreakpoint(QString addr) void CutterCore::toggleBreakpoint(QString addr)
{ {
cmd("db " + addr); cmd("dbs " + addr);
emit instructionChanged(addr.toULongLong()); emit instructionChanged(addr.toULongLong());
emit breakpointsChanged(); emit breakpointsChanged();
} }

View File

@ -497,8 +497,8 @@ public:
void continueUntilDebug(QString offset); void continueUntilDebug(QString offset);
void stepDebug(); void stepDebug();
void stepOverDebug(); void stepOverDebug();
void addBreakpoint(RVA addr); void toggleBreakpoint(RVA addr);
void addBreakpoint(QString addr); void toggleBreakpoint(QString addr);
void delBreakpoint(RVA addr); void delBreakpoint(RVA addr);
void delAllBreakpoints(); void delAllBreakpoints();
void enableBreakpoint(RVA addr); void enableBreakpoint(RVA addr);

View File

@ -324,7 +324,6 @@ FORMS += \
widgets/BacktraceWidget.ui \ widgets/BacktraceWidget.ui \
dialogs/OpenFileDialog.ui \ dialogs/OpenFileDialog.ui \
widgets/MemoryMapWidget.ui \ widgets/MemoryMapWidget.ui \
widgets/MemoryMapWidget.ui \
dialogs/preferences/DebugOptionsWidget.ui \ dialogs/preferences/DebugOptionsWidget.ui \
widgets/BreakpointWidget.ui \ widgets/BreakpointWidget.ui \
dialogs/BreakpointsDialog.ui \ dialogs/BreakpointsDialog.ui \

View File

@ -22,6 +22,7 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
setApplicationName("Cutter"); setApplicationName("Cutter");
setApplicationVersion(APP_VERSION); setApplicationVersion(APP_VERSION);
setWindowIcon(QIcon(":/img/cutter.svg")); setWindowIcon(QIcon(":/img/cutter.svg"));
setAttribute(Qt::AA_DontShowIconsInMenus);
// Set QString codec to UTF-8 // Set QString codec to UTF-8
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));

View File

@ -130,8 +130,19 @@ void MainWindow::initUI()
spacer3->setMaximumWidth(100); spacer3->setMaximumWidth(100);
ui->mainToolBar->addWidget(spacer3); ui->mainToolBar->addWidget(spacer3);
QToolBar *debugToolbar = new DebugToolbar(this); DebugToolbar *debugToolbar = new DebugToolbar(this);
ui->mainToolBar->addWidget(debugToolbar); ui->mainToolBar->addWidget(debugToolbar);
// Debug menu
ui->menuDebug->addAction(debugToolbar->actionStart);
ui->menuDebug->addAction(debugToolbar->actionStartEmul);
ui->menuDebug->addAction(debugToolbar->actionAttach);
ui->menuDebug->addSeparator();
ui->menuDebug->addAction(debugToolbar->actionStep);
ui->menuDebug->addAction(debugToolbar->actionStepOver);
ui->menuDebug->addSeparator();
ui->menuDebug->addAction(debugToolbar->actionContinue);
ui->menuDebug->addAction(debugToolbar->actionContinueUntilCall);
ui->menuDebug->addAction(debugToolbar->actionContinueUntilSyscall);
// Sepparator between undo/redo and goto lineEdit // Sepparator between undo/redo and goto lineEdit
QWidget *spacer4 = new QWidget(); QWidget *spacer4 = new QWidget();

View File

@ -277,10 +277,16 @@ border-top: 0px;
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="addExtraWidgets"/> <addaction name="addExtraWidgets"/>
</widget> </widget>
<widget class="QMenu" name="menuDebug">
<property name="title">
<string>Debug</string>
</property>
</widget>
<addaction name="menuFile"/> <addaction name="menuFile"/>
<addaction name="menuEdit"/> <addaction name="menuEdit"/>
<addaction name="menuView"/> <addaction name="menuView"/>
<addaction name="menuWindows"/> <addaction name="menuWindows"/>
<addaction name="menuDebug"/>
<addaction name="menuHelp"/> <addaction name="menuHelp"/>
</widget> </widget>
<widget class="QToolBar" name="mainToolBar"> <widget class="QToolBar" name="mainToolBar">

View File

@ -113,8 +113,8 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent)
addSeparator(); addSeparator();
debugMenu = new QMenu(tr("Debug"), this); debugMenu = new QMenu(tr("Debug"), this);
debugMenuAction = addMenu(debugMenu); debugMenuAction = addMenu(debugMenu);
actionAddBreakpoint.setText(tr("Add breakpoint")); createAction(debugMenu, &actionAddBreakpoint, tr("Add/remove breakpoint"), getAddBPSequence(),
debugMenu->addAction(&actionAddBreakpoint); SLOT(on_actionAddBreakpoint_triggered()));
actionContinueUntil.setText(tr("Continue until line")); actionContinueUntil.setText(tr("Continue until line"));
debugMenu->addAction(&actionContinueUntil); debugMenu->addAction(&actionContinueUntil);
QString progCounterName = Core()->getRegisterName("PC"); QString progCounterName = Core()->getRegisterName("PC");
@ -148,8 +148,6 @@ 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, connect(&actionContinueUntil, &QAction::triggered,
this, &DisassemblyContextMenu::on_actionContinueUntil_triggered); this, &DisassemblyContextMenu::on_actionContinueUntil_triggered);
connect(&actionSetPC, &QAction::triggered, connect(&actionSetPC, &QAction::triggered,
@ -280,6 +278,11 @@ QKeySequence DisassemblyContextMenu::getDisplayOptionsSequence() const
return {}; //TODO insert correct sequence return {}; //TODO insert correct sequence
} }
QList<QKeySequence> DisassemblyContextMenu::getAddBPSequence() const
{
return {Qt::Key_F2, Qt::CTRL + Qt::Key_B};
}
void DisassemblyContextMenu::on_actionEditInstruction_triggered() void DisassemblyContextMenu::on_actionEditInstruction_triggered()
{ {
EditInstructionDialog *e = new EditInstructionDialog(this); EditInstructionDialog *e = new EditInstructionDialog(this);
@ -354,7 +357,7 @@ void DisassemblyContextMenu::on_actionCopyAddr_triggered()
void DisassemblyContextMenu::on_actionAddBreakpoint_triggered() void DisassemblyContextMenu::on_actionAddBreakpoint_triggered()
{ {
Core()->addBreakpoint(offset); Core()->toggleBreakpoint(offset);
} }
void DisassemblyContextMenu::on_actionContinueUntil_triggered() void DisassemblyContextMenu::on_actionContinueUntil_triggered()
@ -582,3 +585,20 @@ void DisassemblyContextMenu::createAction(QAction *action, QString name, QKeySeq
shortcut->setContext(Qt::WidgetWithChildrenShortcut); shortcut->setContext(Qt::WidgetWithChildrenShortcut);
connect(shortcut, SIGNAL(activated()), this, slot); connect(shortcut, SIGNAL(activated()), this, slot);
} }
void DisassemblyContextMenu::createAction(QMenu *menu, QAction *action, QString name, QList<QKeySequence> keySequence,
const char *slot)
{
action->setText(name);
menu->addAction(action);
action->setShortcuts(keySequence);
connect(action, SIGNAL(triggered(bool)), this, slot);
auto pWidget = parentWidget();
for (auto stct : keySequence) {
QShortcut *shortcut = new QShortcut(stct, pWidget);
shortcut->setContext(Qt::WidgetWithChildrenShortcut);
connect(shortcut, SIGNAL(activated()), this, slot);
}
}

View File

@ -67,6 +67,7 @@ private:
QKeySequence getRenameUsedHereSequence() const; QKeySequence getRenameUsedHereSequence() const;
QKeySequence getXRefSequence() const; QKeySequence getXRefSequence() const;
QKeySequence getDisplayOptionsSequence() const; QKeySequence getDisplayOptionsSequence() const;
QList<QKeySequence> getAddBPSequence() const;
RVA offset; RVA offset;
bool canCopy; bool canCopy;
@ -123,5 +124,6 @@ private:
// 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);
void createAction(QMenu *menu, QAction *action, QString name, QList<QKeySequence> keySequence, const char *slot);
}; };
#endif // DISASSEMBLYCONTEXTMENU_H #endif // DISASSEMBLYCONTEXTMENU_H

View File

@ -191,7 +191,7 @@ void BreakpointWidget::addBreakpointDialog()
if (!bps.isEmpty()) { if (!bps.isEmpty()) {
QStringList bpList = bps.split(" ", QString::SkipEmptyParts); QStringList bpList = bps.split(" ", QString::SkipEmptyParts);
for ( QString bp : bpList) { for ( QString bp : bpList) {
Core()->addBreakpoint(bp); Core()->toggleBreakpoint(bp);
} }
} }
} }

View File

@ -24,15 +24,19 @@ DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) :
QIcon stepOverIcon = QIcon(":/img/icons/step_over_light.svg"); QIcon stepOverIcon = QIcon(":/img/icons/step_over_light.svg");
actionStart = new QAction(startDebugIcon, tr("Start debug"), parent); actionStart = new QAction(startDebugIcon, tr("Start debug"), parent);
actionStart->setShortcut(QKeySequence(Qt::Key_F9));
actionStartEmul = new QAction(startEmulIcon, tr("Start emulation"), parent); actionStartEmul = new QAction(startEmulIcon, tr("Start emulation"), parent);
QAction *actionAttach = new QAction(startAttachIcon, tr("Attach to process"), parent); actionAttach = new QAction(startAttachIcon, tr("Attach to process"), parent);
QAction *actionStop = new QAction(stopIcon, tr("Stop debug"), parent); QAction *actionStop = new QAction(stopIcon, tr("Stop debug"), parent);
QAction *actionContinue = new QAction(continueIcon, tr("Continue"), parent); actionContinue = new QAction(continueIcon, tr("Continue"), parent);
QAction *actionContinueUntilMain = new QAction(continueUntilMainIcon, tr("Continue until main"), parent); actionContinue->setShortcut(QKeySequence(Qt::Key_F5));
QAction *actionContinueUntilCall = new QAction(continueUntilCallIcon, tr("Continue until call"), parent); actionContinueUntilMain = new QAction(continueUntilMainIcon, tr("Continue until main"), parent);
QAction *actionContinueUntilSyscall = new QAction(continueUntilSyscallIcon, tr("Continue until syscall"), parent); actionContinueUntilCall = new QAction(continueUntilCallIcon, tr("Continue until call"), parent);
QAction *actionStep = new QAction(stepIcon, tr("Step"), parent); actionContinueUntilSyscall = new QAction(continueUntilSyscallIcon, tr("Continue until syscall"), parent);
QAction *actionStepOver = new QAction(stepOverIcon, tr("Step over"), parent); actionStep = new QAction(stepIcon, tr("Step"), parent);
actionStep->setShortcut(QKeySequence(Qt::Key_F7));
actionStepOver = new QAction(stepOverIcon, tr("Step over"), parent);
actionStepOver->setShortcut(QKeySequence(Qt::Key_F8));
QToolButton *startButton = new QToolButton; QToolButton *startButton = new QToolButton;
startButton->setPopupMode(QToolButton::MenuButtonPopup); startButton->setPopupMode(QToolButton::MenuButtonPopup);
@ -61,39 +65,39 @@ DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) :
addAction(actionStep); addAction(actionStep);
addAction(actionStepOver); addAction(actionStepOver);
connect(actionStop, &QAction::triggered, Core(), &CutterCore::stopDebug); connect(actionStop, &QAction::triggered, Core(), &CutterCore::stopDebug);
connect(actionStop, &QAction::triggered, [=](){ connect(actionStop, &QAction::triggered, [=]() {
actionContinue->setVisible(true); actionContinue->setVisible(true);
actionStart->setVisible(true); actionStart->setVisible(true);
actionStartEmul->setVisible(true); actionStartEmul->setVisible(true);
actionAttach->setVisible(true); actionAttach->setVisible(true);
actionContinueUntilMain->setVisible(true); actionContinueUntilMain->setVisible(true);
actionContinueUntilCall->setVisible(true); actionContinueUntilCall->setVisible(true);
this->colorToolbar(false); this->colorToolbar(false);
}); });
connect(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug); connect(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug);
connect(actionStart, &QAction::triggered, Core(), &CutterCore::startDebug); connect(actionStart, &QAction::triggered, Core(), &CutterCore::startDebug);
connect(actionStart, &QAction::triggered, [=](){ connect(actionStart, &QAction::triggered, [=]() {
this->colorToolbar(true); this->colorToolbar(true);
actionAttach->setVisible(false); actionAttach->setVisible(false);
actionStartEmul->setVisible(false); actionStartEmul->setVisible(false);
}); });
connect(actionAttach, &QAction::triggered, this, &DebugToolbar::attachProcessDialog); connect(actionAttach, &QAction::triggered, this, &DebugToolbar::attachProcessDialog);
connect(actionStartEmul, &QAction::triggered, Core(), &CutterCore::startEmulation); connect(actionStartEmul, &QAction::triggered, Core(), &CutterCore::startEmulation);
connect(actionStartEmul, &QAction::triggered, [=](){ connect(actionStartEmul, &QAction::triggered, [=]() {
actionContinue->setVisible(false); actionContinue->setVisible(false);
actionStart->setVisible(false); actionStart->setVisible(false);
actionAttach->setVisible(false); actionAttach->setVisible(false);
actionContinueUntilMain->setVisible(false); actionContinueUntilMain->setVisible(false);
actionContinueUntilCall->setVisible(false); actionContinueUntilCall->setVisible(false);
continueUntilButton->setDefaultAction(actionContinueUntilSyscall); continueUntilButton->setDefaultAction(actionContinueUntilSyscall);
this->colorToolbar(true); this->colorToolbar(true);
}); });
connect(actionStepOver, &QAction::triggered, Core(), &CutterCore::stepOverDebug); connect(actionStepOver, &QAction::triggered, Core(), &CutterCore::stepOverDebug);
connect(actionContinue, &QAction::triggered, Core(), &CutterCore::continueDebug); connect(actionContinue, &QAction::triggered, Core(), &CutterCore::continueDebug);
connect(actionContinueUntilMain, &QAction::triggered, this, &DebugToolbar::continueUntilMain); connect(actionContinueUntilMain, &QAction::triggered, this, &DebugToolbar::continueUntilMain);
connect(actionContinueUntilCall, &QAction::triggered, Core(), &CutterCore::continueUntilCall); connect(actionContinueUntilCall, &QAction::triggered, Core(), &CutterCore::continueUntilCall);
connect(actionContinueUntilSyscall, &QAction::triggered, Core(),&CutterCore::continueUntilSyscall); connect(actionContinueUntilSyscall, &QAction::triggered, Core(), &CutterCore::continueUntilSyscall);
} }
void DebugToolbar::continueUntilMain() void DebugToolbar::continueUntilMain()

View File

@ -11,11 +11,18 @@ class DebugToolbar : public QToolBar
public: public:
explicit DebugToolbar(MainWindow *main, QWidget *parent = nullptr); explicit DebugToolbar(MainWindow *main, QWidget *parent = nullptr);
QAction *actionStart;
QAction *actionStartEmul;
QAction *actionAttach;
QAction *actionContinue;
QAction *actionContinueUntilMain;
QAction *actionContinueUntilCall;
QAction *actionContinueUntilSyscall;
QAction *actionStep;
QAction *actionStepOver;
private: private:
MainWindow *main; MainWindow *main;
QAction *actionStart;
QAction *actionStartEmul;
private slots: private slots:
void continueUntilMain(); void continueUntilMain();

View File

@ -693,8 +693,8 @@ void DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEve
seekLocal(instr); seekLocal(instr);
mMenu->setOffset(instr);
if (event->button() == Qt::RightButton) { if (event->button() == Qt::RightButton) {
mMenu->setOffset(instr);
mMenu->exec(event->globalPos()); mMenu->exec(event->globalPos());
} }
} }