From 49d58b36246d3e777d2c50dd172362bfa2c38a1a Mon Sep 17 00:00:00 2001 From: yossizap Date: Wed, 20 Nov 2019 08:50:07 +0000 Subject: [PATCH] gdbserver and windbg remote debugging support (#1874) * Added remote debugging --- src/Cutter.pro | 6 + src/common/R2Task.h | 4 +- src/core/Cutter.cpp | 53 ++++++++ src/core/Cutter.h | 11 ++ src/dialogs/R2TaskDialog.cpp | 72 ++++++++++ src/dialogs/R2TaskDialog.h | 49 +++++++ src/dialogs/R2TaskDialog.ui | 91 +++++++++++++ src/dialogs/RemoteDebugDialog.cpp | 188 ++++++++++++++++++++++++++ src/dialogs/RemoteDebugDialog.h | 48 +++++++ src/dialogs/RemoteDebugDialog.ui | 197 ++++++++++++++++++++++++++++ src/img/icons/play_light_remote.svg | 5 + src/resources.qrc | 1 + src/widgets/DebugActions.cpp | 83 ++++++++++-- src/widgets/DebugActions.h | 11 +- 14 files changed, 804 insertions(+), 15 deletions(-) create mode 100644 src/dialogs/R2TaskDialog.cpp create mode 100644 src/dialogs/R2TaskDialog.h create mode 100644 src/dialogs/R2TaskDialog.ui create mode 100644 src/dialogs/RemoteDebugDialog.cpp create mode 100644 src/dialogs/RemoteDebugDialog.h create mode 100644 src/dialogs/RemoteDebugDialog.ui create mode 100644 src/img/icons/play_light_remote.svg diff --git a/src/Cutter.pro b/src/Cutter.pro index 1940f1f5..222372c1 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -267,6 +267,7 @@ SOURCES += \ dialogs/EditInstructionDialog.cpp \ dialogs/FlagDialog.cpp \ dialogs/RenameDialog.cpp \ + dialogs/RemoteDebugDialog.cpp \ dialogs/XrefsDialog.cpp \ core/MainWindow.cpp \ common/Helpers.cpp \ @@ -335,6 +336,7 @@ SOURCES += \ common/CommandTask.cpp \ common/ProgressIndicator.cpp \ common/R2Task.cpp \ + dialogs/R2TaskDialog.cpp \ widgets/DebugActions.cpp \ widgets/MemoryMapWidget.cpp \ dialogs/preferences/DebugOptionsWidget.cpp \ @@ -398,6 +400,7 @@ HEADERS += \ dialogs/EditInstructionDialog.h \ dialogs/FlagDialog.h \ dialogs/RenameDialog.h \ + dialogs/RemoteDebugDialog.h \ dialogs/XrefsDialog.h \ common/Helpers.h \ common/HexAsciiHighlighter.h \ @@ -469,6 +472,7 @@ HEADERS += \ common/ProgressIndicator.h \ plugins/CutterPlugin.h \ common/R2Task.h \ + dialogs/R2TaskDialog.h \ widgets/DebugActions.h \ widgets/MemoryMapWidget.h \ dialogs/preferences/DebugOptionsWidget.h \ @@ -527,6 +531,7 @@ FORMS += \ dialogs/EditInstructionDialog.ui \ dialogs/FlagDialog.ui \ dialogs/RenameDialog.ui \ + dialogs/RemoteDebugDialog.ui \ dialogs/XrefsDialog.ui \ dialogs/NewfileDialog.ui \ dialogs/InitialOptionsDialog.ui \ @@ -552,6 +557,7 @@ FORMS += \ dialogs/VersionInfoDialog.ui \ widgets/ZignaturesWidget.ui \ dialogs/AsyncTaskDialog.ui \ + dialogs/R2TaskDialog.ui \ widgets/StackWidget.ui \ widgets/RegistersWidget.ui \ widgets/ThreadsWidget.ui \ diff --git a/src/common/R2Task.h b/src/common/R2Task.h index d4efcace..f9aff2a7 100644 --- a/src/common/R2Task.h +++ b/src/common/R2Task.h @@ -15,6 +15,8 @@ private: void taskFinished(); public: + using Ptr = QSharedPointer; + explicit R2Task(const QString &cmd, bool transient = true); ~R2Task(); @@ -30,4 +32,4 @@ signals: void finished(); }; -#endif // R2TASK_H \ No newline at end of file +#endif // R2TASK_H diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index b6a1ec06..736c228e 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -1228,6 +1228,59 @@ void CutterCore::startEmulation() emit debugTaskStateChanged(); } +void CutterCore::attachRemote(const QString &uri) +{ + if (!currentlyDebugging) { + offsetPriorDebugging = getOffset(); + } + + // connect to a debugger with the given plugin + asyncCmd("o-*; e cfg.debug = true; o+ " + uri, debugTask); + emit debugTaskStateChanged(); + + connect(debugTask.data(), &R2Task::finished, this, [this, uri] () { + if (debugTaskDialog) { + delete debugTaskDialog; + } + debugTask.clear(); + // Check if we actually connected + bool connected = false; + QJsonArray openFilesArray = getOpenedFiles(); + for (QJsonValue value : openFilesArray) { + QJsonObject openFile = value.toObject(); + QString fileUri= openFile["uri"].toString(); + if (!fileUri.compare(uri)) { + connected = true; + } + } + QString programCounterValue = cmd("dr?`drn PC`").trimmed(); + seekAndShow(programCounterValue); + if (!connected) { + emit attachedRemote(false); + emit debugTaskStateChanged(); + return; + } + + emit registersChanged(); + if (!currentlyDebugging || !currentlyEmulating) { + // prevent register flags from appearing during debug/emul + setConfig("asm.flags", false); + currentlyDebugging = true; + emit flagsChanged(); + emit changeDebugView(); + } + + emit attachedRemote(true); + emit debugTaskStateChanged(); + }); + + debugTaskDialog = new R2TaskDialog(debugTask); + debugTaskDialog->setBreakOnClose(true); + debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose); + debugTaskDialog->setDesc("Connecting to: " + uri); + debugTaskDialog->show(); +} + void CutterCore::attachDebug(int pid) { if (!currentlyDebugging) { diff --git a/src/core/Cutter.h b/src/core/Cutter.h index 25821db4..4a8fcabe 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -19,10 +19,12 @@ class BasicInstructionHighlighter; class CutterCore; class Decompiler; class R2Task; +class R2TaskDialog; #include "plugins/CutterPlugin.h" #include "common/BasicBlockHighlighter.h" #include "common/R2Task.h" +#include "dialogs/R2TaskDialog.h" #define Core() (CutterCore::instance()) @@ -267,6 +269,12 @@ public: QJsonDocument getBacktrace(); void startDebug(); void startEmulation(); + /** + * @brief attach to a remote debugger + * @param uri remote debugger uri + * @note attachedRemote(bool) signals the result + */ + void attachRemote(const QString &uri); void attachDebug(int pid); void stopDebug(); void suspendDebug(); @@ -473,6 +481,8 @@ signals: void classRenamed(const QString &oldName, const QString &newName); void classAttrsChanged(const QString &cls); + void attachedRemote(bool successfully); + void projectSaved(bool successfully, const QString &name); /** @@ -527,6 +537,7 @@ private: BasicInstructionHighlighter biHighlighter; QSharedPointer debugTask; + R2TaskDialog *debugTaskDialog; }; class RCoreLocked diff --git a/src/dialogs/R2TaskDialog.cpp b/src/dialogs/R2TaskDialog.cpp new file mode 100644 index 00000000..7c30e337 --- /dev/null +++ b/src/dialogs/R2TaskDialog.cpp @@ -0,0 +1,72 @@ +#include "R2TaskDialog.h" +#include "common/R2Task.h" + +#include + +#include "ui_R2TaskDialog.h" + +R2TaskDialog::R2TaskDialog(R2Task::Ptr task, QWidget *parent) + : QDialog(parent), + ui(new Ui::R2TaskDialog), + task(task) +{ + ui->setupUi(this); + + connect(task.data(), &R2Task::finished, this, [this]() { + close(); + }); + + connect(&timer, SIGNAL(timeout()), this, SLOT(updateProgressTimer())); + timer.setInterval(1000); + timer.setSingleShot(false); + timer.start(); + + elapsedTimer.start(); + updateProgressTimer(); +} + +R2TaskDialog::~R2TaskDialog() +{ +} + +void R2TaskDialog::updateProgressTimer() +{ + int secondsElapsed = elapsedTimer.elapsed() / 1000; + int minutesElapsed = secondsElapsed / 60; + int hoursElapsed = minutesElapsed / 60; + + QString label = tr("Running for") + " "; + if (hoursElapsed) { + label += tr("%n hour", "%n hours", hoursElapsed); + label += " "; + } + if (minutesElapsed) { + label += tr("%n minute", "%n minutes", minutesElapsed % 60); + label += " "; + } + label += tr("%n seconds", "%n second", secondsElapsed % 60); + ui->timeLabel->setText(label); +} + +void R2TaskDialog::setDesc(const QString &label) +{ + ui->descLabel->setText(label); +} + +void R2TaskDialog::closeEvent(QCloseEvent *event) +{ + if (breakOnClose) { + task->breakTask(); + setDesc("Attempting to stop the task..."); + event->ignore(); + } else { + QWidget::closeEvent(event); + } +} + +void R2TaskDialog::reject() +{ + task->breakTask(); + setDesc("Attempting to stop the task..."); + +} diff --git a/src/dialogs/R2TaskDialog.h b/src/dialogs/R2TaskDialog.h new file mode 100644 index 00000000..931d5eee --- /dev/null +++ b/src/dialogs/R2TaskDialog.h @@ -0,0 +1,49 @@ +#ifndef R2TASKDIALOG_H +#define R2TASKDIALOG_H + +#include + +#include +#include +#include + +#include "common/R2Task.h" + +class R2Task; +namespace Ui { +class R2TaskDialog; +} + +class R2TaskDialog : public QDialog +{ + Q_OBJECT + +public: + using Ptr = QSharedPointer; + R2TaskDialog(Ptr task, QWidget *parent = nullptr); + ~R2TaskDialog(); + + void setBreakOnClose(bool v) { breakOnClose = v; } + bool getBreakOnClose() { return breakOnClose; } + void setDesc(const QString &label); + +public slots: + void reject() override; + +private slots: + void updateProgressTimer(); + +protected: + void closeEvent(QCloseEvent *event) override; + +private: + std::unique_ptr ui; + QSharedPointer task; + + QTimer timer; + QElapsedTimer elapsedTimer; + + bool breakOnClose = false; +}; + +#endif //R2TASKDIALOG_H diff --git a/src/dialogs/R2TaskDialog.ui b/src/dialogs/R2TaskDialog.ui new file mode 100644 index 00000000..07abd535 --- /dev/null +++ b/src/dialogs/R2TaskDialog.ui @@ -0,0 +1,91 @@ + + + R2TaskDialog + + + + 0 + 0 + 400 + 87 + + + + R2 Task + + + + + + R2 task in progress.. + + + + + + + Time + + + + + + + 0 + + + false + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel + + + + + + + + + buttonBox + accepted() + R2TaskDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + R2TaskDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/dialogs/RemoteDebugDialog.cpp b/src/dialogs/RemoteDebugDialog.cpp new file mode 100644 index 00000000..8e83b705 --- /dev/null +++ b/src/dialogs/RemoteDebugDialog.cpp @@ -0,0 +1,188 @@ +#include "RemoteDebugDialog.h" +#include "ui_RemoteDebugDialog.h" + +#include +#include +#include + +#define GDBSERVER "GDB" +#define WINDBGPIPE "WinDbg - Pipe" +#define WINDBG_URI_PREFIX "windbg" +#define GDB_URI_PREFIX "gdb" +#define DEFAULT_INDEX (GDBSERVER) + +RemoteDebugDialog::RemoteDebugDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::RemoteDebugDialog) +{ + ui->setupUi(this); + setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); + + // Set a default selection + ui->debuggerCombo->setCurrentIndex(ui->debuggerCombo->findText(DEFAULT_INDEX)); + onIndexChange(); + + connect(ui->debuggerCombo, + static_cast(&QComboBox::currentIndexChanged), + this, &RemoteDebugDialog::onIndexChange); +} + +RemoteDebugDialog::~RemoteDebugDialog() {} + +bool RemoteDebugDialog::validate() +{ + QString debugger = getDebugger(); + + if (debugger == GDBSERVER) { + if (!validateIp()) { + return false; + } + if (!validatePort()) { + return false; + } + } else if (debugger == WINDBGPIPE) { + if (!validatePath()) { + return false; + } + } else { + QMessageBox msgBox; + msgBox.setText(tr("Invalid debugger")); + msgBox.exec(); + return false; + } + + return true; +} + +bool RemoteDebugDialog::validateIp() +{ + QMessageBox msgBox; + + QString ip = getIp(); + if (QHostAddress(ip).isNull()) { + msgBox.setText(tr("Invalid IP address")); + msgBox.exec(); + return false; + } + return true; +} + +bool RemoteDebugDialog::validatePath() +{ + QMessageBox msgBox; + + QString path = getPath(); + if (!QFileInfo(path).exists()) { + msgBox.setText(tr("Path does not exist")); + msgBox.exec(); + return false; + } + return true; +} + +bool RemoteDebugDialog::validatePort() +{ + QMessageBox msgBox; + + int port = getPort(); + if (port < 1 || port > 65535) { + msgBox.setText(tr("Invalid port")); + msgBox.exec(); + return false; + } + return true; +} + +void RemoteDebugDialog::onIndexChange() +{ + QString debugger = getDebugger(); + if (debugger == GDBSERVER) { + activateGdb(); + } else if (debugger == WINDBGPIPE) { + activateWinDbgPipe(); + } +} + +void RemoteDebugDialog::activateGdb() +{ + ui->ipEdit->setVisible(true); + ui->portEdit->setVisible(true); + ui->ipText->setVisible(true); + ui->portText->setVisible(true); + ui->pathEdit->setVisible(false); + ui->pathText->setVisible(false); +} + +void RemoteDebugDialog::activateWinDbgPipe() +{ + ui->ipEdit->setVisible(false); + ui->portEdit->setVisible(false); + ui->ipText->setVisible(false); + ui->portText->setVisible(false); + ui->pathEdit->setVisible(true); + ui->pathText->setVisible(true); +} + +void RemoteDebugDialog::on_buttonBox_accepted() +{ +} + +void RemoteDebugDialog::on_buttonBox_rejected() +{ + close(); +} + +void RemoteDebugDialog::setIp(QString ip) +{ + ui->ipEdit->setText(ip); + ui->ipEdit->selectAll(); +} + +void RemoteDebugDialog::setPath(QString path) +{ + ui->pathEdit->setText(path); + ui->pathEdit->selectAll(); +} + +void RemoteDebugDialog::setPort(QString port) +{ + ui->portEdit->setText(port); + ui->portEdit->selectAll(); +} + +void RemoteDebugDialog::setDebugger(QString debugger) +{ + ui->debuggerCombo->setCurrentIndex(ui->debuggerCombo->findText(debugger)); +} + +QString RemoteDebugDialog::getUri() const +{ + QString debugger = getDebugger(); + if (debugger == WINDBGPIPE) { + return QString("%1://%2").arg(WINDBG_URI_PREFIX, getPath()); + } else if (debugger == GDBSERVER) { + return QString("%1://%2:%3").arg(GDB_URI_PREFIX, getIp(), QString::number(getPort())); + } + + return NULL; +} + +QString RemoteDebugDialog::getIp() const +{ + return ui->ipEdit->text(); +} + +QString RemoteDebugDialog::getPath() const +{ + return ui->pathEdit->text(); +} + +int RemoteDebugDialog::getPort() const +{ + return ui->portEdit->text().toInt(); +} + +QString RemoteDebugDialog::getDebugger() const +{ + return ui->debuggerCombo->currentText(); +} diff --git a/src/dialogs/RemoteDebugDialog.h b/src/dialogs/RemoteDebugDialog.h new file mode 100644 index 00000000..8ac5019c --- /dev/null +++ b/src/dialogs/RemoteDebugDialog.h @@ -0,0 +1,48 @@ +#ifndef REMOTEDEBUGDIALOG_H +#define REMOTEDEBUGDIALOG_H + +#include +#include + +namespace Ui { +class RemoteDebugDialog; +} + +/** + * @brief Dialog for connecting to remote debuggers + */ +class RemoteDebugDialog : public QDialog +{ + Q_OBJECT + +public: + explicit RemoteDebugDialog(QWidget *parent = nullptr); + ~RemoteDebugDialog(); + + void setIp(QString ip); + void setPort(QString port); + void setPath(QString path); + void setDebugger(QString debugger); + QString getUri() const; + QString getIp() const; + int getPort() const; + QString getPath() const; + QString getDebugger() const; + bool validate(); + +private slots: + void on_buttonBox_accepted(); + void on_buttonBox_rejected(); + void onIndexChange(); + +private: + void activateGdb(); + void activateWinDbgPipe(); + bool validateIp(); + bool validatePort(); + bool validatePath(); + + std::unique_ptr ui; +}; + +#endif // REMOTE_DEBUG_DIALOG diff --git a/src/dialogs/RemoteDebugDialog.ui b/src/dialogs/RemoteDebugDialog.ui new file mode 100644 index 00000000..566f4760 --- /dev/null +++ b/src/dialogs/RemoteDebugDialog.ui @@ -0,0 +1,197 @@ + + + RemoteDebugDialog + + + Qt::NonModal + + + + 0 + 0 + 320 + 170 + + + + Remote debugging configuration + + + + + 10 + 10 + 300 + 110 + + + + + + + Debugger: + + + + + + + + GDB + + + + + WinDbg - Pipe + + + + + + + + IP: + + + + + + + + 382 + 16777215 + + + + + + + + + + false + + + + + + + + + + Port: + + + + + + + + 382 + 16777215 + + + + + + + + + + false + + + + + + + + + + Path: + + + + + + + + 382 + 16777215 + + + + + + + + + + false + + + + + + + + + + + + 10 + 130 + 301 + 35 + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + buttonBox + accepted() + RemoteDebugDialog + accept() + + + 248 + 234 + + + 157 + 274 + + + + + buttonBox + rejected() + RemoteDebugDialog + reject() + + + 316 + 240 + + + 286 + 254 + + + + + diff --git a/src/img/icons/play_light_remote.svg b/src/img/icons/play_light_remote.svg new file mode 100644 index 00000000..4aa718da --- /dev/null +++ b/src/img/icons/play_light_remote.svg @@ -0,0 +1,5 @@ + + + + R + diff --git a/src/resources.qrc b/src/resources.qrc index c6faca97..74c21b1a 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -24,6 +24,7 @@ img/icons/play_light_debug.svg img/icons/play_light_emul.svg img/icons/play_light_attach.svg + img/icons/play_light_remote.svg img/icons/media-stop_light.svg img/icons/media-suspend_light.svg img/icons/media-skip-forward_light.svg diff --git a/src/widgets/DebugActions.cpp b/src/widgets/DebugActions.cpp index f47a2ed6..6669e0a2 100644 --- a/src/widgets/DebugActions.cpp +++ b/src/widgets/DebugActions.cpp @@ -20,6 +20,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QIcon startDebugIcon = QIcon(":/img/icons/play_light_debug.svg"); 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 stopIcon = QIcon(":/img/icons/media-stop_light.svg"); QIcon continueUntilMainIcon = QIcon(":/img/icons/continue_until_main.svg"); QIcon continueUntilCallIcon = QIcon(":/img/icons/continue_until_call.svg"); @@ -35,6 +36,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QString startDebugLabel = tr("Start debug"); 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 restartDebugLabel = tr("Restart program"); @@ -53,6 +55,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : actionStart->setShortcut(QKeySequence(Qt::Key_F9)); actionStartEmul = new QAction(startEmulIcon, startEmulLabel, this); actionAttach = new QAction(startAttachIcon, startAttachLabel, this); + actionStartRemote = new QAction(startRemoteIcon, startRemoteLabel, this); actionStop = new QAction(stopIcon, stopDebugLabel, this); actionContinue = new QAction(continueIcon, continueLabel, this); actionContinue->setShortcut(QKeySequence(Qt::Key_F5)); @@ -75,8 +78,8 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : startMenu->addAction(actionStart); startMenu->addAction(actionStartEmul); startMenu->addAction(actionAttach); + startMenu->addAction(actionStartRemote); startButton->setDefaultAction(actionStart); - // startButton->setDefaultAction(actionStartEmul); startButton->setMenu(startMenu); continueUntilButton = new QToolButton; @@ -105,21 +108,28 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : // Toggle all buttons except restart, suspend(=continue) and stop since those are // necessary to avoid staying stuck - toggleActions = { actionStep, actionStepOver, actionStepOut, actionContinueUntilMain, + toggleActions = {actionStep, actionStepOver, actionStepOut, actionContinueUntilMain, actionContinueUntilCall, actionContinueUntilSyscall}; + toggleConnectionActions = {actionAttach, actionStart, actionStartRemote, actionStartEmul}; connect(Core(), &CutterCore::debugTaskStateChanged, this, [ = ]() { - bool disableToolbar = Core()->isDebugTaskInProgress() || !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); + 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 { - actionContinue->setText(continueLabel); - actionContinue->setIcon(continueIcon); + for (QAction *a : toggleConnectionActions) { + a->setDisabled(disableToolbar); + } } }); @@ -128,6 +138,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : actionStart->setVisible(true); actionStartEmul->setVisible(true); actionAttach->setVisible(true); + actionStartRemote->setVisible(true); actionStop->setText(stopDebugLabel); actionStart->setText(startDebugLabel); actionStart->setIcon(startDebugIcon); @@ -149,6 +160,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : } setAllActionsVisible(true); actionAttach->setVisible(false); + actionStartRemote->setVisible(false); actionStartEmul->setVisible(false); actionStart->setText(restartDebugLabel); actionStart->setIcon(restartIcon); @@ -157,11 +169,14 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : }); 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); connect(actionStartEmul, &QAction::triggered, [ = ]() { setAllActionsVisible(true); actionStart->setVisible(false); actionAttach->setVisible(false); + actionStartRemote->setVisible(false); actionContinueUntilMain->setVisible(false); actionStepOut->setVisible(false); continueUntilButton->setDefaultAction(actionContinueUntilSyscall); @@ -200,6 +215,49 @@ void DebugActions::continueUntilMain() 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); +} + +void DebugActions::onAttachedRemoteDebugger(bool successfully) { + if (!successfully) { + QMessageBox msgBox; + msgBox.setText(tr("Error connecting.")); + msgBox.exec(); + attachRemoteDialog(); + } else { + delete remoteDialog; + attachRemoteDebugger(); + } +} + +void DebugActions::attachRemoteDialog() +{ + 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()); + } + } +} + void DebugActions::attachProcessDialog() { AttachProcDialog dialog(main); @@ -226,6 +284,7 @@ void DebugActions::attachProcess(int pid) // hide unwanted buttons setAllActionsVisible(true); actionStart->setVisible(false); + actionStartRemote->setVisible(false); actionStartEmul->setVisible(false); actionStop->setText(stopAttachLabel); // attach diff --git a/src/widgets/DebugActions.h b/src/widgets/DebugActions.h index 69860e8d..915c99bd 100644 --- a/src/widgets/DebugActions.h +++ b/src/widgets/DebugActions.h @@ -1,6 +1,7 @@ #pragma once #include "core/Cutter.h" +#include "dialogs/RemoteDebugDialog.h" #include @@ -18,6 +19,7 @@ public: void addToToolBar(QToolBar *toolBar); QAction *actionStart; + QAction *actionStartRemote; QAction *actionStartEmul; QAction *actionAttach; QAction *actionContinue; @@ -29,7 +31,7 @@ public: QAction *actionStepOut; QAction *actionStop; QAction *actionAllContinues; - + // Continue and suspend interchange during runtime QIcon continueIcon; QIcon suspendIcon; @@ -41,14 +43,19 @@ private: * @brief buttons that will be disabled/enabled on (disable/enable)DebugToolbar */ QList toggleActions; - MainWindow *main; + QList toggleConnectionActions; QList allActions; QToolButton *continueUntilButton; + RemoteDebugDialog *remoteDialog = nullptr; + MainWindow *main; private slots: void continueUntilMain(); void attachProcessDialog(); void attachProcess(int pid); + void attachRemoteDialog(); + void attachRemoteDebugger(); + void onAttachedRemoteDebugger(bool successfully); void setAllActionsVisible(bool visible); void setButtonVisibleIfMainExists(); };