Add reverse debugging (#1918)

* Add initial reverse debugging support

* Add reverse debug icons

* Added an option to stop the debug session and enabled continue and step back

* Added a new icon for stop trace and recolored start trace

* Toggle reverse debug actions when not tracing

* Stop existing trace sessions in stopDebug

* Ported to Rizin

* Set reverse icons to invisible when not in a trace session instead of disabled

* Updated rizin submodule

* Cleaned up step and continue events

* Apply clang format
This commit is contained in:
yossizap 2021-02-13 17:35:57 +00:00 committed by GitHub
parent 3b8f98ca42
commit 8f89d1641b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 356 additions and 14 deletions

2
rizin

@ -1 +1 @@
Subproject commit 74ca7ff0d1a95d3604b1900311e18741af097ec4
Subproject commit 380372dce7cc7bf6244f135d10c73a426a098167

View File

@ -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));

View File

@ -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;

View File

@ -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();

View File

@ -0,0 +1,8 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<metadata id="metadata5379">image/svg+xml</metadata>
<g class="layer">
<title>Layer 1</title>
<path clip-rule="evenodd" d="m15.833328,2.249998l-1.46,0l0,0.24001l0,11.75999l1.46,0l0,-12zm-3.406667,0.18094l0,11.81906l-8.76,-5.93782l8.76,-5.88124zm-6.146405,5.88124l4.686182,-3.06218l0,6.1809l-4.686182,-3.11872z" fill="#75beff" fill-rule="evenodd" id="svg_1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 482 B

View File

@ -0,0 +1,6 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g class="layer">
<title>Layer 1</title>
<path clip-rule="evenodd" d="m1.731978,6.075001l0,-4l1.518314,0l0,2.5416c1.158979,-1.35857 2.94652,-2.20826 4.899932,-2.20826c3.214756,0 5.992004,2.30702 6.233608,5.39741l0.021054,0.26925l-1.519407,0l-0.022147,-0.2259c-0.215125,-2.19478 -2.229097,-3.94076 -4.713108,-3.94076c-1.757256,0 -3.283232,0.87591 -4.099072,2.16666l2.863448,0l0,1.5l-4.212925,0l-0.973239,-0.97507l0,-0.52493l0.003543,0zm6.326306,8.25c-1.118056,0 -2.024418,-0.8954 -2.024418,-2c0,-1.1046 0.906362,-2 2.024418,-2c1.118056,0 2.024418,0.8954 2.024418,2c0,1.1046 -0.906362,2 -2.024418,2z" fill="#75BEFF" fill-rule="evenodd" id="svg_4"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 768 B

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="500px"
height="500px"
viewBox="0 0 500 500"
enable-background="new 0 0 500 500"
xml:space="preserve"
sodipodi:docname="stop_trace.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><metadata
id="metadata13"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs11" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1017"
id="namedview9"
showgrid="true"
inkscape:zoom="0.944"
inkscape:cx="49.481017"
inkscape:cy="170.83775"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<g
id="g4"
style="fill:#78de82;fill-opacity:1">
<path
enable-background="new "
d="M366.566,249.441c0.229,64.494-51.64,116.898-115.929,117.123 c-64.571,0.227-117.042-51.708-117.203-116.005c-0.162-64.701,51.523-116.88,116.016-117.124 C314.081,133.191,366.336,184.951,366.566,249.441z"
id="path2"
fill="#78de82"
opacity="0.75"
style="fill:#78de82;fill-opacity:1" />
</g>
<path
opacity="0.75"
fill="#78de82"
enable-background="new "
d="M250,70.443c-99.165,0-179.557,80.392-179.557,179.557 S150.835,429.557,250,429.557S429.557,349.165,429.557,250S349.165,70.443,250,70.443z M250,399.837 c-83.303,0-150.82-67.518-150.82-150.82S166.697,98.196,250,98.196s150.82,67.528,150.82,150.82S333.303,399.837,250,399.837z"
id="path6"
style="fill:#78de82;fill-opacity:0.74901962" />
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="500px"
height="500px"
viewBox="0 0 500 500"
enable-background="new 0 0 500 500"
xml:space="preserve"
sodipodi:docname="stop_trace.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><metadata
id="metadata13"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs11" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1017"
id="namedview9"
showgrid="true"
inkscape:zoom="0.472"
inkscape:cx="250"
inkscape:cy="250"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1"><inkscape:grid
type="xygrid"
id="grid17" /></sodipodi:namedview>
<path
opacity="0.75"
fill="#b15858"
enable-background="new "
d="M250,70.443c-99.165,0-179.557,80.392-179.557,179.557 S150.835,429.557,250,429.557S429.557,349.165,429.557,250S349.165,70.443,250,70.443z M250,399.837 c-83.303,0-150.82-67.518-150.82-150.82S166.697,98.196,250,98.196s150.82,67.528,150.82,150.82S333.303,399.837,250,399.837z"
id="path6" />
<rect
id="rect15"
width="179.15254"
height="180.00002"
x="160.84744"
y="160"
style="fill:#ff3d3d;fill-opacity:0.74901961;stroke-width:0.87863451" /></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -33,6 +33,7 @@
<file>img/icons/light/continue_until_call.svg</file>
<file>img/icons/continue_until_syscall.svg</file>
<file>img/icons/light/continue_until_syscall.svg</file>
<file>img/icons/reverse_continue.svg</file>
<file>img/icons/detach_debugger.svg</file>
<file>img/icons/light/step_into.svg</file>
<file>img/icons/step_into.svg</file>
@ -40,6 +41,9 @@
<file>img/icons/step_over.svg</file>
<file>img/icons/step_out.svg</file>
<file>img/icons/light/step_out.svg</file>
<file>img/icons/reverse_step.svg</file>
<file>img/icons/start_trace.svg</file>
<file>img/icons/stop_trace.svg</file>
<file>img/icons/cloud.svg</file>
<file>img/icons/down.svg</file>
<file>img/icons/down_white.svg</file>

View File

@ -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();
}

View File

@ -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<QAction *> toggleActions;
QList<QAction *> toggleConnectionActions;
QList<QAction *> reverseActions;
QList<QAction *> allActions;
QToolButton *continueUntilButton;
RemoteDebugDialog *remoteDialog = nullptr;