gdbserver and windbg remote debugging support (#1874)

* Added remote debugging
This commit is contained in:
yossizap 2019-11-20 08:50:07 +00:00 committed by Itay Cohen
parent c85e1db3b3
commit 49d58b3624
14 changed files with 804 additions and 15 deletions

View File

@ -267,6 +267,7 @@ SOURCES += \
dialogs/EditInstructionDialog.cpp \ dialogs/EditInstructionDialog.cpp \
dialogs/FlagDialog.cpp \ dialogs/FlagDialog.cpp \
dialogs/RenameDialog.cpp \ dialogs/RenameDialog.cpp \
dialogs/RemoteDebugDialog.cpp \
dialogs/XrefsDialog.cpp \ dialogs/XrefsDialog.cpp \
core/MainWindow.cpp \ core/MainWindow.cpp \
common/Helpers.cpp \ common/Helpers.cpp \
@ -335,6 +336,7 @@ SOURCES += \
common/CommandTask.cpp \ common/CommandTask.cpp \
common/ProgressIndicator.cpp \ common/ProgressIndicator.cpp \
common/R2Task.cpp \ common/R2Task.cpp \
dialogs/R2TaskDialog.cpp \
widgets/DebugActions.cpp \ widgets/DebugActions.cpp \
widgets/MemoryMapWidget.cpp \ widgets/MemoryMapWidget.cpp \
dialogs/preferences/DebugOptionsWidget.cpp \ dialogs/preferences/DebugOptionsWidget.cpp \
@ -398,6 +400,7 @@ HEADERS += \
dialogs/EditInstructionDialog.h \ dialogs/EditInstructionDialog.h \
dialogs/FlagDialog.h \ dialogs/FlagDialog.h \
dialogs/RenameDialog.h \ dialogs/RenameDialog.h \
dialogs/RemoteDebugDialog.h \
dialogs/XrefsDialog.h \ dialogs/XrefsDialog.h \
common/Helpers.h \ common/Helpers.h \
common/HexAsciiHighlighter.h \ common/HexAsciiHighlighter.h \
@ -469,6 +472,7 @@ HEADERS += \
common/ProgressIndicator.h \ common/ProgressIndicator.h \
plugins/CutterPlugin.h \ plugins/CutterPlugin.h \
common/R2Task.h \ common/R2Task.h \
dialogs/R2TaskDialog.h \
widgets/DebugActions.h \ widgets/DebugActions.h \
widgets/MemoryMapWidget.h \ widgets/MemoryMapWidget.h \
dialogs/preferences/DebugOptionsWidget.h \ dialogs/preferences/DebugOptionsWidget.h \
@ -527,6 +531,7 @@ FORMS += \
dialogs/EditInstructionDialog.ui \ dialogs/EditInstructionDialog.ui \
dialogs/FlagDialog.ui \ dialogs/FlagDialog.ui \
dialogs/RenameDialog.ui \ dialogs/RenameDialog.ui \
dialogs/RemoteDebugDialog.ui \
dialogs/XrefsDialog.ui \ dialogs/XrefsDialog.ui \
dialogs/NewfileDialog.ui \ dialogs/NewfileDialog.ui \
dialogs/InitialOptionsDialog.ui \ dialogs/InitialOptionsDialog.ui \
@ -552,6 +557,7 @@ FORMS += \
dialogs/VersionInfoDialog.ui \ dialogs/VersionInfoDialog.ui \
widgets/ZignaturesWidget.ui \ widgets/ZignaturesWidget.ui \
dialogs/AsyncTaskDialog.ui \ dialogs/AsyncTaskDialog.ui \
dialogs/R2TaskDialog.ui \
widgets/StackWidget.ui \ widgets/StackWidget.ui \
widgets/RegistersWidget.ui \ widgets/RegistersWidget.ui \
widgets/ThreadsWidget.ui \ widgets/ThreadsWidget.ui \

View File

@ -15,6 +15,8 @@ private:
void taskFinished(); void taskFinished();
public: public:
using Ptr = QSharedPointer<R2Task>;
explicit R2Task(const QString &cmd, bool transient = true); explicit R2Task(const QString &cmd, bool transient = true);
~R2Task(); ~R2Task();

View File

@ -1228,6 +1228,59 @@ void CutterCore::startEmulation()
emit debugTaskStateChanged(); 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) void CutterCore::attachDebug(int pid)
{ {
if (!currentlyDebugging) { if (!currentlyDebugging) {

View File

@ -19,10 +19,12 @@ class BasicInstructionHighlighter;
class CutterCore; class CutterCore;
class Decompiler; class Decompiler;
class R2Task; class R2Task;
class R2TaskDialog;
#include "plugins/CutterPlugin.h" #include "plugins/CutterPlugin.h"
#include "common/BasicBlockHighlighter.h" #include "common/BasicBlockHighlighter.h"
#include "common/R2Task.h" #include "common/R2Task.h"
#include "dialogs/R2TaskDialog.h"
#define Core() (CutterCore::instance()) #define Core() (CutterCore::instance())
@ -267,6 +269,12 @@ public:
QJsonDocument getBacktrace(); QJsonDocument getBacktrace();
void startDebug(); void startDebug();
void startEmulation(); 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 attachDebug(int pid);
void stopDebug(); void stopDebug();
void suspendDebug(); void suspendDebug();
@ -473,6 +481,8 @@ signals:
void classRenamed(const QString &oldName, const QString &newName); void classRenamed(const QString &oldName, const QString &newName);
void classAttrsChanged(const QString &cls); void classAttrsChanged(const QString &cls);
void attachedRemote(bool successfully);
void projectSaved(bool successfully, const QString &name); void projectSaved(bool successfully, const QString &name);
/** /**
@ -527,6 +537,7 @@ private:
BasicInstructionHighlighter biHighlighter; BasicInstructionHighlighter biHighlighter;
QSharedPointer<R2Task> debugTask; QSharedPointer<R2Task> debugTask;
R2TaskDialog *debugTaskDialog;
}; };
class RCoreLocked class RCoreLocked

View File

@ -0,0 +1,72 @@
#include "R2TaskDialog.h"
#include "common/R2Task.h"
#include <QCloseEvent>
#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...");
}

View File

@ -0,0 +1,49 @@
#ifndef R2TASKDIALOG_H
#define R2TASKDIALOG_H
#include <memory>
#include <QDialog>
#include <QTimer>
#include <QElapsedTimer>
#include "common/R2Task.h"
class R2Task;
namespace Ui {
class R2TaskDialog;
}
class R2TaskDialog : public QDialog
{
Q_OBJECT
public:
using Ptr = QSharedPointer<R2Task>;
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::R2TaskDialog> ui;
QSharedPointer<R2Task> task;
QTimer timer;
QElapsedTimer elapsedTimer;
bool breakOnClose = false;
};
#endif //R2TASKDIALOG_H

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>R2TaskDialog</class>
<widget class="QDialog" name="R2TaskDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>87</height>
</rect>
</property>
<property name="windowTitle">
<string>R2 Task</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="descLabel">
<property name="text">
<string>R2 task in progress..</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="timeLabel">
<property name="text">
<string>Time</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="maximum">
<number>0</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>R2TaskDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>R2TaskDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,188 @@
#include "RemoteDebugDialog.h"
#include "ui_RemoteDebugDialog.h"
#include <QHostAddress>
#include <QFileInfo>
#include <QMessageBox>
#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<void (QComboBox::*)(int)>(&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();
}

View File

@ -0,0 +1,48 @@
#ifndef REMOTEDEBUGDIALOG_H
#define REMOTEDEBUGDIALOG_H
#include <QDialog>
#include <memory>
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::RemoteDebugDialog> ui;
};
#endif // REMOTE_DEBUG_DIALOG

View File

@ -0,0 +1,197 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RemoteDebugDialog</class>
<widget class="QDialog" name="RemoteDebugDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>320</width>
<height>170</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Remote debugging configuration</string>
</property>
<widget class="QWidget" name="gridLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>300</width>
<height>110</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="debugText">
<property name="text">
<string>Debugger:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="debuggerCombo">
<item>
<property name="text">
<string>GDB</string>
</property>
</item>
<item>
<property name="text">
<string>WinDbg - Pipe</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="ipText">
<property name="text">
<string>IP:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="ipEdit">
<property name="maximumSize">
<size>
<width>382</width>
<height>16777215</height>
</size>
</property>
<property name="inputMask">
<string notr="true"/>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="frame">
<bool>false</bool>
</property>
<property name="placeholderText">
<string notr="true"/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="portText">
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="portEdit">
<property name="maximumSize">
<size>
<width>382</width>
<height>16777215</height>
</size>
</property>
<property name="inputMask">
<string notr="true"/>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="frame">
<bool>false</bool>
</property>
<property name="placeholderText">
<string notr="true"/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="pathText">
<property name="text">
<string>Path:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="pathEdit">
<property name="maximumSize">
<size>
<width>382</width>
<height>16777215</height>
</size>
</property>
<property name="inputMask">
<string notr="true"/>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="frame">
<bool>false</bool>
</property>
<property name="placeholderText">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>130</y>
<width>301</width>
<height>35</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>RemoteDebugDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>234</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>RemoteDebugDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>240</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>254</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,5 @@
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
<svg style="enable-background:new 0 0 24 32" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="32px" width="24px" version="1.1" y="0px" x="0px" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 32">
<polygon points="0 0 24 16 0 32" fill="#78de82"/>
<text x="15" y="10" font-family="Helvetica, Arial, sans-serif" font-size="13" stroke-width="2" stroke="#aaacaf" fill="#aaacaf" >R</text>
</svg>

After

Width:  |  Height:  |  Size: 526 B

View File

@ -24,6 +24,7 @@
<file>img/icons/play_light_debug.svg</file> <file>img/icons/play_light_debug.svg</file>
<file>img/icons/play_light_emul.svg</file> <file>img/icons/play_light_emul.svg</file>
<file>img/icons/play_light_attach.svg</file> <file>img/icons/play_light_attach.svg</file>
<file>img/icons/play_light_remote.svg</file>
<file>img/icons/media-stop_light.svg</file> <file>img/icons/media-stop_light.svg</file>
<file>img/icons/media-suspend_light.svg</file> <file>img/icons/media-suspend_light.svg</file>
<file>img/icons/media-skip-forward_light.svg</file> <file>img/icons/media-skip-forward_light.svg</file>

View File

@ -20,6 +20,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
QIcon startDebugIcon = QIcon(":/img/icons/play_light_debug.svg"); QIcon startDebugIcon = QIcon(":/img/icons/play_light_debug.svg");
QIcon startEmulIcon = QIcon(":/img/icons/play_light_emul.svg"); QIcon startEmulIcon = QIcon(":/img/icons/play_light_emul.svg");
QIcon startAttachIcon = QIcon(":/img/icons/play_light_attach.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 stopIcon = QIcon(":/img/icons/media-stop_light.svg");
QIcon continueUntilMainIcon = QIcon(":/img/icons/continue_until_main.svg"); QIcon continueUntilMainIcon = QIcon(":/img/icons/continue_until_main.svg");
QIcon continueUntilCallIcon = QIcon(":/img/icons/continue_until_call.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 startDebugLabel = tr("Start debug");
QString startEmulLabel = tr("Start emulation"); QString startEmulLabel = tr("Start emulation");
QString startAttachLabel = tr("Attach to process"); QString startAttachLabel = tr("Attach to process");
QString startRemoteLabel = tr("Connect to a remote debugger");
QString stopDebugLabel = tr("Stop debug"); QString stopDebugLabel = tr("Stop debug");
QString stopEmulLabel = tr("Stop emulation"); QString stopEmulLabel = tr("Stop emulation");
QString restartDebugLabel = tr("Restart program"); QString restartDebugLabel = tr("Restart program");
@ -53,6 +55,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
actionStart->setShortcut(QKeySequence(Qt::Key_F9)); actionStart->setShortcut(QKeySequence(Qt::Key_F9));
actionStartEmul = new QAction(startEmulIcon, startEmulLabel, this); actionStartEmul = new QAction(startEmulIcon, startEmulLabel, this);
actionAttach = new QAction(startAttachIcon, startAttachLabel, this); actionAttach = new QAction(startAttachIcon, startAttachLabel, this);
actionStartRemote = new QAction(startRemoteIcon, startRemoteLabel, this);
actionStop = new QAction(stopIcon, stopDebugLabel, this); actionStop = new QAction(stopIcon, stopDebugLabel, this);
actionContinue = new QAction(continueIcon, continueLabel, this); actionContinue = new QAction(continueIcon, continueLabel, this);
actionContinue->setShortcut(QKeySequence(Qt::Key_F5)); actionContinue->setShortcut(QKeySequence(Qt::Key_F5));
@ -75,8 +78,8 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
startMenu->addAction(actionStart); startMenu->addAction(actionStart);
startMenu->addAction(actionStartEmul); startMenu->addAction(actionStartEmul);
startMenu->addAction(actionAttach); startMenu->addAction(actionAttach);
startMenu->addAction(actionStartRemote);
startButton->setDefaultAction(actionStart); startButton->setDefaultAction(actionStart);
// startButton->setDefaultAction(actionStartEmul);
startButton->setMenu(startMenu); startButton->setMenu(startMenu);
continueUntilButton = new QToolButton; continueUntilButton = new QToolButton;
@ -105,11 +108,13 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
// Toggle all buttons except restart, suspend(=continue) and stop since those are // Toggle all buttons except restart, suspend(=continue) and stop since those are
// necessary to avoid staying stuck // necessary to avoid staying stuck
toggleActions = { actionStep, actionStepOver, actionStepOut, actionContinueUntilMain, toggleActions = {actionStep, actionStepOver, actionStepOut, actionContinueUntilMain,
actionContinueUntilCall, actionContinueUntilSyscall}; actionContinueUntilCall, actionContinueUntilSyscall};
toggleConnectionActions = {actionAttach, actionStart, actionStartRemote, actionStartEmul};
connect(Core(), &CutterCore::debugTaskStateChanged, this, [ = ]() { connect(Core(), &CutterCore::debugTaskStateChanged, this, [ = ]() {
bool disableToolbar = Core()->isDebugTaskInProgress() || !Core()->currentlyDebugging; bool disableToolbar = Core()->isDebugTaskInProgress();
if (Core()->currentlyDebugging) {
for (QAction *a : toggleActions) { for (QAction *a : toggleActions) {
a->setDisabled(disableToolbar); a->setDisabled(disableToolbar);
} }
@ -121,6 +126,11 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
actionContinue->setText(continueLabel); actionContinue->setText(continueLabel);
actionContinue->setIcon(continueIcon); actionContinue->setIcon(continueIcon);
} }
} else {
for (QAction *a : toggleConnectionActions) {
a->setDisabled(disableToolbar);
}
}
}); });
connect(actionStop, &QAction::triggered, Core(), &CutterCore::stopDebug); connect(actionStop, &QAction::triggered, Core(), &CutterCore::stopDebug);
@ -128,6 +138,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
actionStart->setVisible(true); actionStart->setVisible(true);
actionStartEmul->setVisible(true); actionStartEmul->setVisible(true);
actionAttach->setVisible(true); actionAttach->setVisible(true);
actionStartRemote->setVisible(true);
actionStop->setText(stopDebugLabel); actionStop->setText(stopDebugLabel);
actionStart->setText(startDebugLabel); actionStart->setText(startDebugLabel);
actionStart->setIcon(startDebugIcon); actionStart->setIcon(startDebugIcon);
@ -149,6 +160,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
} }
setAllActionsVisible(true); setAllActionsVisible(true);
actionAttach->setVisible(false); actionAttach->setVisible(false);
actionStartRemote->setVisible(false);
actionStartEmul->setVisible(false); actionStartEmul->setVisible(false);
actionStart->setText(restartDebugLabel); actionStart->setText(restartDebugLabel);
actionStart->setIcon(restartIcon); actionStart->setIcon(restartIcon);
@ -157,11 +169,14 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
}); });
connect(actionAttach, &QAction::triggered, this, &DebugActions::attachProcessDialog); 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, Core(), &CutterCore::startEmulation);
connect(actionStartEmul, &QAction::triggered, [ = ]() { connect(actionStartEmul, &QAction::triggered, [ = ]() {
setAllActionsVisible(true); setAllActionsVisible(true);
actionStart->setVisible(false); actionStart->setVisible(false);
actionAttach->setVisible(false); actionAttach->setVisible(false);
actionStartRemote->setVisible(false);
actionContinueUntilMain->setVisible(false); actionContinueUntilMain->setVisible(false);
actionStepOut->setVisible(false); actionStepOut->setVisible(false);
continueUntilButton->setDefaultAction(actionContinueUntilSyscall); continueUntilButton->setDefaultAction(actionContinueUntilSyscall);
@ -200,6 +215,49 @@ void DebugActions::continueUntilMain()
Core()->continueUntilDebug(mainAddr); 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() void DebugActions::attachProcessDialog()
{ {
AttachProcDialog dialog(main); AttachProcDialog dialog(main);
@ -226,6 +284,7 @@ void DebugActions::attachProcess(int pid)
// hide unwanted buttons // hide unwanted buttons
setAllActionsVisible(true); setAllActionsVisible(true);
actionStart->setVisible(false); actionStart->setVisible(false);
actionStartRemote->setVisible(false);
actionStartEmul->setVisible(false); actionStartEmul->setVisible(false);
actionStop->setText(stopAttachLabel); actionStop->setText(stopAttachLabel);
// attach // attach

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "core/Cutter.h" #include "core/Cutter.h"
#include "dialogs/RemoteDebugDialog.h"
#include <QAction> #include <QAction>
@ -18,6 +19,7 @@ public:
void addToToolBar(QToolBar *toolBar); void addToToolBar(QToolBar *toolBar);
QAction *actionStart; QAction *actionStart;
QAction *actionStartRemote;
QAction *actionStartEmul; QAction *actionStartEmul;
QAction *actionAttach; QAction *actionAttach;
QAction *actionContinue; QAction *actionContinue;
@ -41,14 +43,19 @@ private:
* @brief buttons that will be disabled/enabled on (disable/enable)DebugToolbar * @brief buttons that will be disabled/enabled on (disable/enable)DebugToolbar
*/ */
QList<QAction *> toggleActions; QList<QAction *> toggleActions;
MainWindow *main; QList<QAction *> toggleConnectionActions;
QList<QAction *> allActions; QList<QAction *> allActions;
QToolButton *continueUntilButton; QToolButton *continueUntilButton;
RemoteDebugDialog *remoteDialog = nullptr;
MainWindow *main;
private slots: private slots:
void continueUntilMain(); void continueUntilMain();
void attachProcessDialog(); void attachProcessDialog();
void attachProcess(int pid); void attachProcess(int pid);
void attachRemoteDialog();
void attachRemoteDebugger();
void onAttachedRemoteDebugger(bool successfully);
void setAllActionsVisible(bool visible); void setAllActionsVisible(bool visible);
void setButtonVisibleIfMainExists(); void setButtonVisibleIfMainExists();
}; };