cutter/src/widgets/DebugActions.cpp

362 lines
13 KiB
C++
Raw Normal View History

2018-12-21 20:36:40 +00:00
#include "DebugActions.h"
#include "core/MainWindow.h"
#include "dialogs/AttachProcDialog.h"
2019-12-11 11:26:54 +00:00
#include "dialogs/NativeDebugDialog.h"
2019-12-07 14:15:09 +00:00
#include "common/Configuration.h"
#include "common/Helpers.h"
2018-06-12 08:43:14 +00:00
#include <QPainter>
#include <QMenu>
2018-10-03 11:30:12 +00:00
#include <QList>
2018-07-25 08:59:40 +00:00
#include <QFileInfo>
2018-12-21 20:36:40 +00:00
#include <QToolBar>
#include <QToolButton>
2018-06-12 08:43:14 +00:00
2018-12-21 20:36:40 +00:00
DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
QObject(main),
2018-06-12 08:43:14 +00:00
main(main)
{
2018-12-21 20:36:40 +00:00
setObjectName("DebugActions");
// setIconSize(QSize(16, 16));
2018-10-03 11:30:12 +00:00
// define icons
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");
2019-12-07 13:28:05 +00:00
stopIcon = QIcon(":/img/icons/media-stop_light.svg");
2019-12-11 11:26:54 +00:00
restartIcon = QIcon(":/img/icons/spin_light.svg");
2019-12-07 13:28:05 +00:00
detachIcon = QIcon(":/img/icons/detach_debugger.svg");
2019-12-11 11:26:54 +00:00
startDebugIcon = QIcon(":/img/icons/play_light_debug.svg");
continueIcon = QIcon(":/img/icons/media-skip-forward_light.svg");
suspendIcon = QIcon(":/img/icons/media-suspend_light.svg");
2018-06-12 08:43:14 +00:00
// define action labels
QString startEmulLabel = tr("Start emulation");
QString startAttachLabel = tr("Attach to process");
QString startRemoteLabel = tr("Connect to a remote debugger");
QString stopDebugLabel = tr("Stop debug");
QString stopEmulLabel = tr("Stop emulation");
QString restartEmulLabel = tr("Restart emulation");
QString continueUMLabel = tr("Continue until main");
QString continueUCLabel = tr("Continue until call");
QString continueUSLabel = tr("Continue until syscall");
QString stepLabel = tr("Step");
QString stepOverLabel = tr("Step over");
QString stepOutLabel = tr("Step out");
2019-12-11 11:26:54 +00:00
suspendLabel = tr("Suspend the process");
continueLabel = tr("Continue");
2019-12-11 11:26:54 +00:00
restartDebugLabel = tr("Restart program");
startDebugLabel = tr("Start debug");
2018-10-03 11:30:12 +00:00
// define actions
2018-12-21 20:36:40 +00:00
actionStart = new QAction(startDebugIcon, startDebugLabel, this);
actionStart->setShortcut(QKeySequence(Qt::Key_F9));
2018-12-21 20:36:40 +00:00
actionStartEmul = new QAction(startEmulIcon, startEmulLabel, this);
actionAttach = new QAction(startAttachIcon, startAttachLabel, this);
actionStartRemote = new QAction(startRemoteIcon, startRemoteLabel, this);
2018-12-21 20:36:40 +00:00
actionStop = new QAction(stopIcon, stopDebugLabel, this);
actionContinue = new QAction(continueIcon, continueLabel, this);
actionContinue->setShortcut(QKeySequence(Qt::Key_F5));
2019-12-07 14:15:09 +00:00
actionContinueUntilMain = new QAction(continueUMLabel, this);
actionContinueUntilCall = new QAction(continueUCLabel, this);
actionContinueUntilSyscall = new QAction(continueUSLabel, this);
actionStep = new QAction(stepLabel, this);
actionStep->setShortcut(QKeySequence(Qt::Key_F7));
2019-12-07 14:15:09 +00:00
actionStepOver = new QAction(stepOverLabel, this);
actionStepOver->setShortcut(QKeySequence(Qt::Key_F8));
2019-12-07 14:15:09 +00:00
actionStepOut = new QAction(stepOutLabel, this);
actionStepOut->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_F8));
QToolButton *startButton = new QToolButton;
startButton->setPopupMode(QToolButton::MenuButtonPopup);
connect(startButton, &QToolButton::triggered, startButton, &QToolButton::setDefaultAction);
QMenu *startMenu = new QMenu(startButton);
2018-10-03 11:30:12 +00:00
// only emulation is currently allowed
2018-10-03 21:41:52 +00:00
startMenu->addAction(actionStart);
2018-10-03 11:30:12 +00:00
startMenu->addAction(actionStartEmul);
2018-10-03 21:41:52 +00:00
startMenu->addAction(actionAttach);
startMenu->addAction(actionStartRemote);
2018-10-03 21:41:52 +00:00
startButton->setDefaultAction(actionStart);
startButton->setMenu(startMenu);
continueUntilButton = new QToolButton;
continueUntilButton->setPopupMode(QToolButton::MenuButtonPopup);
2018-09-30 20:00:53 +00:00
connect(continueUntilButton, &QToolButton::triggered, continueUntilButton,
&QToolButton::setDefaultAction);
QMenu *continueUntilMenu = new QMenu(continueUntilButton);
continueUntilMenu->addAction(actionContinueUntilMain);
continueUntilMenu->addAction(actionContinueUntilCall);
continueUntilMenu->addAction(actionContinueUntilSyscall);
continueUntilButton->setMenu(continueUntilMenu);
continueUntilButton->setDefaultAction(actionContinueUntilMain);
2018-10-03 11:30:12 +00:00
// define toolbar widgets and actions
2018-12-21 20:36:40 +00:00
toolBar->addWidget(startButton);
toolBar->addAction(actionContinue);
toolBar->addAction(actionStop);
actionAllContinues = toolBar->addWidget(continueUntilButton);
toolBar->addAction(actionStepOver);
2019-12-07 13:28:05 +00:00
toolBar->addAction(actionStep);
2018-12-21 20:36:40 +00:00
toolBar->addAction(actionStepOut);
2018-06-12 08:43:14 +00:00
2018-10-03 11:30:12 +00:00
allActions = {actionStop, actionAllContinues, actionContinue, actionContinueUntilCall, actionContinueUntilMain, actionContinueUntilSyscall, actionStep, actionStepOut, actionStepOver};
// hide allactions
setAllActionsVisible(false);
// Toggle all buttons except restart, suspend(=continue) and stop since those are
// necessary to avoid staying stuck
2019-12-07 14:15:09 +00:00
toggleActions = {actionStepOver, actionStep, actionStepOut, actionContinueUntilMain,
actionContinueUntilCall, actionContinueUntilSyscall};
toggleConnectionActions = {actionAttach, actionStartRemote};
connect(Core(), &CutterCore::debugTaskStateChanged, this, [ = ]() {
bool disableToolbar = Core()->isDebugTaskInProgress();
if (Core()->currentlyDebugging) {
for (QAction *a : toggleActions) {
a->setDisabled(disableToolbar);
}
// Suspend should only be available when other icons are disabled
if (disableToolbar) {
actionContinue->setText(suspendLabel);
actionContinue->setIcon(suspendIcon);
} else {
actionContinue->setText(continueLabel);
actionContinue->setIcon(continueIcon);
}
} else {
for (QAction *a : toggleConnectionActions) {
a->setDisabled(disableToolbar);
}
}
});
connect(actionStop, &QAction::triggered, Core(), &CutterCore::stopDebug);
2018-09-30 20:00:53 +00:00
connect(actionStop, &QAction::triggered, [ = ]() {
actionStart->setVisible(true);
actionStartEmul->setVisible(true);
actionAttach->setVisible(true);
actionStartRemote->setVisible(true);
actionStop->setText(stopDebugLabel);
2019-12-07 13:28:05 +00:00
actionStop->setIcon(stopIcon);
actionStart->setText(startDebugLabel);
2018-10-03 11:30:12 +00:00
actionStart->setIcon(startDebugIcon);
actionStartEmul->setText(startEmulLabel);
2018-10-03 11:30:12 +00:00
actionStartEmul->setIcon(startEmulIcon);
continueUntilButton->setDefaultAction(actionContinueUntilMain);
2018-10-03 11:30:12 +00:00
setAllActionsVisible(false);
});
connect(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug);
2019-12-11 11:26:54 +00:00
connect(actionStart, &QAction::triggered, this, &DebugActions::startDebug);
2018-10-03 11:30:12 +00:00
2018-12-21 20:36:40 +00:00
connect(actionAttach, &QAction::triggered, this, &DebugActions::attachProcessDialog);
connect(actionStartRemote, &QAction::triggered, this, &DebugActions::attachRemoteDialog);
connect(Core(), &CutterCore::attachedRemote, this, &DebugActions::onAttachedRemoteDebugger);
connect(actionStartEmul, &QAction::triggered, Core(), &CutterCore::startEmulation);
2018-09-30 20:00:53 +00:00
connect(actionStartEmul, &QAction::triggered, [ = ]() {
2018-10-03 11:30:12 +00:00
setAllActionsVisible(true);
actionStart->setVisible(false);
actionAttach->setVisible(false);
actionStartRemote->setVisible(false);
actionContinueUntilMain->setVisible(false);
actionStepOut->setVisible(false);
continueUntilButton->setDefaultAction(actionContinueUntilSyscall);
actionStartEmul->setText(restartEmulLabel);
2018-10-03 11:30:12 +00:00
actionStartEmul->setIcon(restartIcon);
actionStop->setText(stopEmulLabel);
});
connect(actionStepOver, &QAction::triggered, Core(), &CutterCore::stepOverDebug);
connect(actionStepOut, &QAction::triggered, Core(), &CutterCore::stepOutDebug);
2018-12-21 20:36:40 +00:00
connect(actionContinueUntilMain, &QAction::triggered, this, &DebugActions::continueUntilMain);
connect(actionContinueUntilCall, &QAction::triggered, Core(), &CutterCore::continueUntilCall);
connect(actionContinueUntilSyscall, &QAction::triggered, Core(), &CutterCore::continueUntilSyscall);
connect(actionContinue, &QAction::triggered, Core(), [=]() {
// Switch between continue and suspend depending on the debugger's state
if (Core()->isDebugTaskInProgress()) {
Core()->suspendDebug();
} else {
Core()->continueDebug();
}
});
2019-12-07 14:15:09 +00:00
connect(Config(), &Configuration::interfaceThemeChanged, this, &DebugActions::chooseThemeIcons);
chooseThemeIcons();
2018-06-12 08:43:14 +00:00
}
void DebugActions::setButtonVisibleIfMainExists()
{
int mainExists = Core()->cmd("f?sym.main; ??").toInt();
// if main is not a flag we hide the continue until main button
if (!mainExists) {
actionContinueUntilMain->setVisible(false);
continueUntilButton->setDefaultAction(actionContinueUntilCall);
}
}
2019-12-11 11:26:54 +00:00
void DebugActions::showDebugWarning()
{
if (!acceptedDebugWarning) {
acceptedDebugWarning = true;
QMessageBox msgBox;
msgBox.setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
msgBox.setText(tr("Debug is currently in beta.\n") +
tr("If you encounter any problems or have suggestions, please submit an issue to https://github.com/radareorg/cutter/issues"));
msgBox.exec();
}
}
2018-12-21 20:36:40 +00:00
void DebugActions::continueUntilMain()
2018-06-12 08:43:14 +00:00
{
QString mainAddr = Core()->cmd("?v sym.main");
Core()->continueUntilDebug(mainAddr);
}
void DebugActions::attachRemoteDebugger()
{
QString stopAttachLabel = tr("Detach from process");
// Hide unwanted buttons
setAllActionsVisible(true);
actionStart->setVisible(false);
actionStartRemote->setVisible(false);
actionStartEmul->setVisible(false);
actionStop->setText(stopAttachLabel);
}
2019-12-11 11:26:54 +00:00
void DebugActions::onAttachedRemoteDebugger(bool successfully)
{
if (!successfully) {
QMessageBox msgBox;
msgBox.setText(tr("Error connecting."));
msgBox.exec();
attachRemoteDialog();
} else {
delete remoteDialog;
attachRemoteDebugger();
}
}
void DebugActions::attachRemoteDialog()
{
2019-12-11 11:26:54 +00:00
showDebugWarning();
if (!remoteDialog) {
remoteDialog = new RemoteDebugDialog(main);
}
QMessageBox msgBox;
bool success = false;
while (!success) {
success = true;
if (remoteDialog->exec()) {
if (!remoteDialog->validate()) {
success = false;
continue;
}
Core()->attachRemote(remoteDialog->getUri());
}
}
}
2018-12-21 20:36:40 +00:00
void DebugActions::attachProcessDialog()
{
2019-12-11 11:26:54 +00:00
showDebugWarning();
AttachProcDialog dialog(main);
bool success = false;
while (!success) {
success = true;
if (dialog.exec()) {
int pid = dialog.getPID();
if (pid >= 0) {
attachProcess(pid);
} else {
success = false;
QMessageBox msgBox;
msgBox.setText(tr("Error attaching. No process selected!"));
msgBox.exec();
}
}
}
}
2018-12-21 20:36:40 +00:00
void DebugActions::attachProcess(int pid)
{
QString stopAttachLabel = tr("Detach from process");
// hide unwanted buttons
2018-10-03 11:30:12 +00:00
setAllActionsVisible(true);
actionStart->setVisible(false);
actionStartRemote->setVisible(false);
actionStartEmul->setVisible(false);
actionStop->setText(stopAttachLabel);
2019-12-07 13:28:05 +00:00
actionStop->setIcon(detachIcon);
// attach
Core()->attachDebug(pid);
2018-10-03 11:30:12 +00:00
}
2019-12-11 11:26:54 +00:00
void DebugActions::startDebug()
{
// check if file is executable before starting debug
QString filename = Core()->getConfig("file.path").section(QLatin1Char(' '), 0, 0);
QFileInfo info(filename);
if (!Core()->currentlyDebugging && !info.isExecutable()) {
QMessageBox msgBox;
msgBox.setText(tr("File '%1' does not have executable permissions.").arg(filename));
msgBox.exec();
return;
}
showDebugWarning();
NativeDebugDialog dialog(main);
dialog.setArgs(Core()->getConfig("dbg.args"));
QString args;
if (dialog.exec()) {
args = dialog.getArgs();
} else {
return;
}
// Update dbg.args with the new args
Core()->setConfig("dbg.args", args);
setAllActionsVisible(true);
actionAttach->setVisible(false);
actionStartRemote->setVisible(false);
actionStartEmul->setVisible(false);
actionStart->setText(restartDebugLabel);
actionStart->setIcon(restartIcon);
setButtonVisibleIfMainExists();
Core()->startDebug();
}
2018-12-21 20:36:40 +00:00
void DebugActions::setAllActionsVisible(bool visible)
2018-10-03 11:30:12 +00:00
{
for (QAction *action : allActions) {
action->setVisible(visible);
}
2018-10-04 13:37:12 +00:00
}
2019-12-07 14:15:09 +00:00
/**
2019-12-08 07:19:58 +00:00
* @brief When theme changed, change icons which have a special version for the theme.
2019-12-07 14:15:09 +00:00
*/
void DebugActions::chooseThemeIcons()
{
// List of QActions which have alternative icons in different themes
const QList<QPair<void*, QString>> kSupportedIconsNames {
{ actionStep, QStringLiteral("step_into.svg") },
{ actionStepOver, QStringLiteral("step_over.svg") },
{ actionStepOut, QStringLiteral("step_out.svg") },
{ actionContinueUntilMain, QStringLiteral("continue_until_main.svg") },
{ actionContinueUntilCall, QStringLiteral("continue_until_call.svg") },
{ actionContinueUntilSyscall, QStringLiteral("continue_until_syscall.svg") },
};
// Set the correct icon for the QAction
qhelpers::setThemeIcons(kSupportedIconsNames, [](void *obj, const QIcon &icon) {
static_cast<QAction*>(obj)->setIcon(icon);
});
2019-12-11 11:26:54 +00:00
}