diff --git a/rizin b/rizin index 74ca7ff0..380372dc 160000 --- a/rizin +++ b/rizin @@ -1 +1 @@ -Subproject commit 74ca7ff0d1a95d3604b1900311e18741af097ec4 +Subproject commit 380372dce7cc7bf6244f135d10c73a426a098167 diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index b1018047..24946fad 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -1865,10 +1865,11 @@ void CutterCore::stopDebug() } currentlyDebugging = false; + currentlyTracing = false; emit debugTaskStateChanged(); if (currentlyEmulating) { - cmdEsil("aeim-; aei-; wcr; .ar-"); + cmdEsil("aeim-; aei-; wcr; .ar-; aets-"); currentlyEmulating = false; } else if (currentlyAttachedToPID != -1) { // Use cmd because cmdRaw would not work with command concatenation @@ -1926,7 +1927,33 @@ void CutterCore::continueDebug() connect(debugTask.data(), &RizinTask::finished, this, [this]() { debugTask.clear(); syncAndSeekProgramCounter(); - emit registersChanged(); + emit refreshCodeViews(); + emit debugTaskStateChanged(); + }); + + debugTask->startTask(); +} + +void CutterCore::continueBackDebug() +{ + if (!currentlyDebugging) { + return; + } + + if (currentlyEmulating) { + if (!asyncCmdEsil("aecb", debugTask)) { + return; + } + } else { + if (!asyncCmd("dcb", debugTask)) { + return; + } + } + emit debugTaskStateChanged(); + + connect(debugTask.data(), &RizinTask::finished, this, [this]() { + debugTask.clear(); + syncAndSeekProgramCounter(); emit refreshCodeViews(); emit debugTaskStateChanged(); }); @@ -1954,8 +1981,6 @@ void CutterCore::continueUntilDebug(QString offset) connect(debugTask.data(), &RizinTask::finished, this, [this]() { debugTask.clear(); syncAndSeekProgramCounter(); - emit registersChanged(); - emit stackChanged(); emit refreshCodeViews(); emit debugTaskStateChanged(); }); @@ -1983,6 +2008,7 @@ void CutterCore::continueUntilCall() connect(debugTask.data(), &RizinTask::finished, this, [this]() { debugTask.clear(); syncAndSeekProgramCounter(); + emit refreshCodeViews(); emit debugTaskStateChanged(); }); @@ -2009,6 +2035,7 @@ void CutterCore::continueUntilSyscall() connect(debugTask.data(), &RizinTask::finished, this, [this]() { debugTask.clear(); syncAndSeekProgramCounter(); + emit refreshCodeViews(); emit debugTaskStateChanged(); }); @@ -2035,6 +2062,7 @@ void CutterCore::stepDebug() connect(debugTask.data(), &RizinTask::finished, this, [this]() { debugTask.clear(); syncAndSeekProgramCounter(); + emit refreshCodeViews(); emit debugTaskStateChanged(); }); @@ -2061,6 +2089,7 @@ void CutterCore::stepOverDebug() connect(debugTask.data(), &RizinTask::finished, this, [this]() { debugTask.clear(); syncAndSeekProgramCounter(); + emit refreshCodeViews(); emit debugTaskStateChanged(); }); @@ -2081,6 +2110,34 @@ void CutterCore::stepOutDebug() connect(debugTask.data(), &RizinTask::finished, this, [this]() { debugTask.clear(); syncAndSeekProgramCounter(); + emit refreshCodeViews(); + emit debugTaskStateChanged(); + }); + + debugTask->startTask(); +} + +void CutterCore::stepBackDebug() +{ + if (!currentlyDebugging) { + return; + } + + if (currentlyEmulating) { + if (!asyncCmdEsil("aesb", debugTask)) { + return; + } + } else { + if (!asyncCmd("dsb", debugTask)) { + return; + } + } + emit debugTaskStateChanged(); + + connect(debugTask.data(), &RizinTask::finished, this, [this]() { + debugTask.clear(); + syncAndSeekProgramCounter(); + emit refreshCodeViews(); emit debugTaskStateChanged(); }); @@ -2112,6 +2169,78 @@ void CutterCore::setDebugPlugin(QString plugin) setConfig("dbg.backend", plugin); } +void CutterCore::startTraceSession() +{ + if (!currentlyDebugging || currentlyTracing) { + return; + } + + if (currentlyEmulating) { + if (!asyncCmdEsil("aets+", debugTask)) { + return; + } + } else { + if (!asyncCmd("dts+", debugTask)) { + return; + } + } + emit debugTaskStateChanged(); + + connect(debugTask.data(), &RizinTask::finished, this, [this]() { + if (debugTaskDialog) { + delete debugTaskDialog; + } + debugTask.clear(); + + currentlyTracing = true; + emit debugTaskStateChanged(); + }); + + debugTaskDialog = new RizinTaskDialog(debugTask); + debugTaskDialog->setBreakOnClose(true); + debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose); + debugTaskDialog->setDesc(tr("Creating debug tracepoint...")); + debugTaskDialog->show(); + + debugTask->startTask(); +} + +void CutterCore::stopTraceSession() +{ + if (!currentlyDebugging || !currentlyTracing) { + return; + } + + if (currentlyEmulating) { + if (!asyncCmdEsil("aets-", debugTask)) { + return; + } + } else { + if (!asyncCmd("dts-", debugTask)) { + return; + } + } + emit debugTaskStateChanged(); + + connect(debugTask.data(), &RizinTask::finished, this, [this]() { + if (debugTaskDialog) { + delete debugTaskDialog; + } + debugTask.clear(); + + currentlyTracing = false; + emit debugTaskStateChanged(); + }); + + debugTaskDialog = new RizinTaskDialog(debugTask); + debugTaskDialog->setBreakOnClose(true); + debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose); + debugTaskDialog->setDesc(tr("Stopping debug session...")); + debugTaskDialog->show(); + + debugTask->startTask(); +} + void CutterCore::toggleBreakpoint(RVA addr) { cmdRaw(QString("dbs %1").arg(addr)); diff --git a/src/core/Cutter.h b/src/core/Cutter.h index 2a0ba6dd..a577ee9a 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -409,12 +409,17 @@ public: void suspendDebug(); void syncAndSeekProgramCounter(); void continueDebug(); + void continueBackDebug(); void continueUntilCall(); void continueUntilSyscall(); void continueUntilDebug(QString offset); void stepDebug(); void stepOverDebug(); void stepOutDebug(); + void stepBackDebug(); + + void startTraceSession(); + void stopTraceSession(); void addBreakpoint(const BreakpointDescription &config); void updateBreakpoint(int index, const BreakpointDescription &config); @@ -449,6 +454,7 @@ public: bool isRedirectableDebugee(); bool currentlyDebugging = false; bool currentlyEmulating = false; + bool currentlyTracing = false; int currentlyAttachedToPID = -1; QString currentlyOpenFile; diff --git a/src/core/MainWindow.cpp b/src/core/MainWindow.cpp index 76735810..0b422623 100644 --- a/src/core/MainWindow.cpp +++ b/src/core/MainWindow.cpp @@ -296,10 +296,14 @@ void MainWindow::initToolBar() ui->menuDebug->addAction(debugActions->actionStep); ui->menuDebug->addAction(debugActions->actionStepOver); ui->menuDebug->addAction(debugActions->actionStepOut); + ui->menuDebug->addAction(debugActions->actionStepBack); ui->menuDebug->addSeparator(); ui->menuDebug->addAction(debugActions->actionContinue); ui->menuDebug->addAction(debugActions->actionContinueUntilCall); ui->menuDebug->addAction(debugActions->actionContinueUntilSyscall); + ui->menuDebug->addAction(debugActions->actionContinueBack); + ui->menuDebug->addSeparator(); + ui->menuDebug->addAction(debugActions->actionTrace); // Sepparator between undo/redo and goto lineEdit QWidget *spacer4 = new QWidget(); diff --git a/src/img/icons/reverse_continue.svg b/src/img/icons/reverse_continue.svg new file mode 100644 index 00000000..43d98f61 --- /dev/null +++ b/src/img/icons/reverse_continue.svg @@ -0,0 +1,8 @@ + + image/svg+xml + + + Layer 1 + + + diff --git a/src/img/icons/reverse_step.svg b/src/img/icons/reverse_step.svg new file mode 100644 index 00000000..a9f4ed2d --- /dev/null +++ b/src/img/icons/reverse_step.svg @@ -0,0 +1,6 @@ + + + Layer 1 + + + diff --git a/src/img/icons/start_trace.svg b/src/img/icons/start_trace.svg new file mode 100644 index 00000000..96d55d77 --- /dev/null +++ b/src/img/icons/start_trace.svg @@ -0,0 +1,62 @@ + +image/svg+xml + + + + + diff --git a/src/img/icons/stop_trace.svg b/src/img/icons/stop_trace.svg new file mode 100644 index 00000000..65805f35 --- /dev/null +++ b/src/img/icons/stop_trace.svg @@ -0,0 +1,59 @@ + +image/svg+xml + + + diff --git a/src/resources.qrc b/src/resources.qrc index 19989a98..f7cc7b12 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -33,6 +33,7 @@ img/icons/light/continue_until_call.svg img/icons/continue_until_syscall.svg img/icons/light/continue_until_syscall.svg + img/icons/reverse_continue.svg img/icons/detach_debugger.svg img/icons/light/step_into.svg img/icons/step_into.svg @@ -40,6 +41,9 @@ img/icons/step_over.svg img/icons/step_out.svg img/icons/light/step_out.svg + img/icons/reverse_step.svg + img/icons/start_trace.svg + img/icons/stop_trace.svg img/icons/cloud.svg img/icons/down.svg img/icons/down_white.svg diff --git a/src/widgets/DebugActions.cpp b/src/widgets/DebugActions.cpp index 2de927f6..b0f34d9c 100644 --- a/src/widgets/DebugActions.cpp +++ b/src/widgets/DebugActions.cpp @@ -22,6 +22,10 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QObject(main), QIcon startEmulIcon = QIcon(":/img/icons/play_light_emul.svg"); QIcon startAttachIcon = QIcon(":/img/icons/play_light_attach.svg"); QIcon startRemoteIcon = QIcon(":/img/icons/play_light_remote.svg"); + QIcon continueBackIcon = QIcon(":/img/icons/reverse_continue.svg"); + QIcon stepBackIcon = QIcon(":/img/icons/reverse_step.svg"); + startTraceIcon = QIcon(":/img/icons/start_trace.svg"); + stopTraceIcon = QIcon(":/img/icons/stop_trace.svg"); stopIcon = QIcon(":/img/icons/media-stop_light.svg"); restartIcon = QIcon(":/img/icons/spin_light.svg"); detachIcon = QIcon(":/img/icons/detach_debugger.svg"); @@ -39,9 +43,13 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QObject(main), QString continueUMLabel = tr("Continue until main"); QString continueUCLabel = tr("Continue until call"); QString continueUSLabel = tr("Continue until syscall"); + QString continueBackLabel = tr("Continue backwards"); QString stepLabel = tr("Step"); QString stepOverLabel = tr("Step over"); QString stepOutLabel = tr("Step out"); + QString stepBackLabel = tr("Step backwards"); + startTraceLabel = tr("Start trace session"); + stopTraceLabel = tr("Stop trace session"); suspendLabel = tr("Suspend the process"); continueLabel = tr("Continue"); restartDebugLabel = tr("Restart program"); @@ -59,19 +67,23 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QObject(main), actionContinueUntilMain = new QAction(continueUMLabel, this); actionContinueUntilCall = new QAction(continueUCLabel, this); actionContinueUntilSyscall = new QAction(continueUSLabel, this); + actionContinueBack = new QAction(continueBackIcon, continueBackLabel, this); + actionContinueBack->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_F5)); actionStep = new QAction(stepLabel, this); actionStep->setShortcut(QKeySequence(Qt::Key_F7)); actionStepOver = new QAction(stepOverLabel, this); actionStepOver->setShortcut(QKeySequence(Qt::Key_F8)); actionStepOut = new QAction(stepOutLabel, this); actionStepOut->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_F8)); + actionStepBack = new QAction(stepBackIcon, stepBackLabel, this); + actionStepBack->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_F7)); + actionTrace = new QAction(startTraceIcon, startTraceLabel, this); QToolButton *startButton = new QToolButton; startButton->setPopupMode(QToolButton::MenuButtonPopup); connect(startButton, &QToolButton::triggered, startButton, &QToolButton::setDefaultAction); QMenu *startMenu = new QMenu(startButton); - // only emulation is currently allowed startMenu->addAction(actionStart); startMenu->addAction(actionStartEmul); startMenu->addAction(actionAttach); @@ -98,6 +110,9 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QObject(main), toolBar->addAction(actionStepOver); toolBar->addAction(actionStep); toolBar->addAction(actionStepOut); + toolBar->addAction(actionStepBack); + toolBar->addAction(actionContinueBack); + toolBar->addAction(actionTrace); allActions = { actionStop, actionAllContinues, @@ -107,16 +122,25 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QObject(main), actionContinueUntilSyscall, actionStep, actionStepOut, - actionStepOver }; - // hide allactions + actionStepOver, + actionContinueBack, + actionStepBack, + actionTrace }; + + // Hide all actions setAllActionsVisible(false); - // Toggle all buttons except restart, suspend(=continue) and stop since those are - // necessary to avoid staying stuck - toggleActions = { actionStepOver, actionStep, - actionStepOut, actionContinueUntilMain, - actionContinueUntilCall, actionContinueUntilSyscall }; + // Toggle all buttons except reverse step/continue which are handled separately and + // restart, suspend(=continue) and stop since those are necessary to avoid freezing + toggleActions = { actionStepOver, + actionStep, + actionStepOut, + actionContinueUntilMain, + actionContinueUntilCall, + actionContinueUntilSyscall, + actionTrace }; toggleConnectionActions = { actionAttach, actionStartRemote }; + reverseActions = { actionStepBack, actionContinueBack }; connect(Core(), &CutterCore::debugProcessFinished, this, [=](int pid) { QMessageBox msgBox; @@ -138,6 +162,10 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QObject(main), actionContinue->setText(continueLabel); actionContinue->setIcon(continueIcon); } + for (QAction *a : reverseActions) { + a->setVisible(Core()->currentlyTracing); + a->setDisabled(disableToolbar); + } } else { for (QAction *a : toggleConnectionActions) { a->setDisabled(disableToolbar); @@ -160,7 +188,10 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QObject(main), continueUntilButton->setDefaultAction(actionContinueUntilMain); setAllActionsVisible(false); }); + connect(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug); + connect(actionStepBack, &QAction::triggered, Core(), &CutterCore::stepBackDebug); + connect(actionStart, &QAction::triggered, this, &DebugActions::startDebug); connect(actionAttach, &QAction::triggered, this, &DebugActions::attachProcessDialog); @@ -178,6 +209,10 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QObject(main), actionStartEmul->setText(restartEmulLabel); actionStartEmul->setIcon(restartIcon); actionStop->setText(stopEmulLabel); + // Reverse debug actions aren't visible until we start tracing + for (QAction *a : reverseActions) { + a->setVisible(false); + } }); connect(actionStepOver, &QAction::triggered, Core(), &CutterCore::stepOverDebug); connect(actionStepOut, &QAction::triggered, Core(), &CutterCore::stepOutDebug); @@ -185,6 +220,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QObject(main), connect(actionContinueUntilCall, &QAction::triggered, Core(), &CutterCore::continueUntilCall); connect(actionContinueUntilSyscall, &QAction::triggered, Core(), &CutterCore::continueUntilSyscall); + connect(actionContinueBack, &QAction::triggered, Core(), &CutterCore::continueBackDebug); connect(actionContinue, &QAction::triggered, Core(), [=]() { // Switch between continue and suspend depending on the debugger's state if (Core()->isDebugTaskInProgress()) { @@ -194,6 +230,19 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QObject(main), } }); + connect(actionTrace, &QAction::triggered, Core(), [=]() { + // Check if a debug session was created to switch between start and stop + if (!Core()->currentlyTracing) { + Core()->startTraceSession(); + actionTrace->setText(stopTraceLabel); + actionTrace->setIcon(stopTraceIcon); + } else { + Core()->stopTraceSession(); + actionTrace->setText(startTraceLabel); + actionTrace->setIcon(startTraceIcon); + } + }); + connect(Config(), &Configuration::interfaceThemeChanged, this, &DebugActions::chooseThemeIcons); chooseThemeIcons(); } @@ -352,6 +401,13 @@ void DebugActions::startDebug() actionStart->setIcon(restartIcon); setButtonVisibleIfMainExists(); + // Reverse debug actions aren't visible until we start tracing + for (QAction *a : reverseActions) { + a->setVisible(false); + } + actionTrace->setText(startTraceLabel); + actionTrace->setIcon(startTraceIcon); + Core()->startDebug(); } diff --git a/src/widgets/DebugActions.h b/src/widgets/DebugActions.h index 4ba280e6..ac5bc92b 100644 --- a/src/widgets/DebugActions.h +++ b/src/widgets/DebugActions.h @@ -26,21 +26,28 @@ public: QAction *actionContinueUntilMain; QAction *actionContinueUntilCall; QAction *actionContinueUntilSyscall; + QAction *actionContinueBack; QAction *actionStep; QAction *actionStepOver; QAction *actionStepOut; + QAction *actionStepBack; QAction *actionStop; QAction *actionAllContinues; + QAction *actionTrace; // Continue/suspend and start/restart interchange during runtime QIcon continueIcon; QIcon suspendIcon; QIcon restartIcon; QIcon startDebugIcon; - QString suspendLabel; + QIcon startTraceIcon; + QIcon stopTraceIcon; QString continueLabel; + QString suspendLabel; QString restartDebugLabel; QString startDebugLabel; + QString startTraceLabel; + QString stopTraceLabel; // Stop and Detach interchange during runtime QIcon detachIcon; @@ -52,6 +59,7 @@ private: */ QList toggleActions; QList toggleConnectionActions; + QList reverseActions; QList allActions; QToolButton *continueUntilButton; RemoteDebugDialog *remoteDialog = nullptr;