From e236f6b0fc85f66b2cd3f8f96b304798dd99bec9 Mon Sep 17 00:00:00 2001 From: xarkes Date: Wed, 18 Nov 2020 13:15:36 +0100 Subject: [PATCH] Remote Debug dialog improvements (#2472) Co-authored-by: nk521 --- src/dialogs/RemoteDebugDialog.cpp | 239 +++++++++++++++--------- src/dialogs/RemoteDebugDialog.h | 55 ++++-- src/dialogs/RemoteDebugDialog.ui | 293 ++++++++++++++++-------------- src/widgets/DebugActions.cpp | 20 +- 4 files changed, 359 insertions(+), 248 deletions(-) diff --git a/src/dialogs/RemoteDebugDialog.cpp b/src/dialogs/RemoteDebugDialog.cpp index 8e83b705..a2a9bef5 100644 --- a/src/dialogs/RemoteDebugDialog.cpp +++ b/src/dialogs/RemoteDebugDialog.cpp @@ -4,12 +4,23 @@ #include #include #include +#include -#define GDBSERVER "GDB" -#define WINDBGPIPE "WinDbg - Pipe" -#define WINDBG_URI_PREFIX "windbg" -#define GDB_URI_PREFIX "gdb" -#define DEFAULT_INDEX (GDBSERVER) +enum DbgBackendType { + GDB = 0, + WINDBG = 1 +}; + +struct DbgBackend { + DbgBackendType type; + QString name; + QString prefix; +}; + +static const QList dbgBackends = { + { GDB, "GDB", "gdb://" }, + { WINDBG, "WinKd - Pipe", "winkd://" } +}; RemoteDebugDialog::RemoteDebugDialog(QWidget *parent) : QDialog(parent), @@ -18,47 +29,45 @@ RemoteDebugDialog::RemoteDebugDialog(QWidget *parent) : ui->setupUi(this); setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); - // Set a default selection - ui->debuggerCombo->setCurrentIndex(ui->debuggerCombo->findText(DEFAULT_INDEX)); - onIndexChange(); + // Fill in debugger Combo + ui->debuggerCombo->clear(); + for (auto& backend : dbgBackends) { + ui->debuggerCombo->addItem(backend.name); + } - connect(ui->debuggerCombo, - static_cast(&QComboBox::currentIndexChanged), - this, &RemoteDebugDialog::onIndexChange); + // Fill ip list + fillRecentIpList(); + ui->ipEdit->setFocus(); + + // Connect statements for right click action and itemClicked action + ui->recentsIpListWidget->addAction(ui->actionRemoveItem); + ui->recentsIpListWidget->addAction(ui->actionRemoveAll); + connect(ui->actionRemoveAll, &QAction::triggered, this, &RemoteDebugDialog::clearAll); + connect(ui->actionRemoveItem, &QAction::triggered, this, &RemoteDebugDialog::removeItem); + connect(ui->recentsIpListWidget, &QListWidget::itemClicked, this, &RemoteDebugDialog::itemClicked); } 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; + int debugger = getDebugger(); + if (debugger == GDB) { + return validatePort() && validateIp(); + } else if (debugger == WINDBG) { + return validatePath(); } - - return true; + QMessageBox msgBox; + msgBox.setText(tr("Invalid debugger")); + msgBox.exec(); + return false; } bool RemoteDebugDialog::validateIp() { QMessageBox msgBox; - QString ip = getIp(); + QString ip = getIpOrPath(); if (QHostAddress(ip).isNull()) { msgBox.setText(tr("Invalid IP address")); msgBox.exec(); @@ -71,7 +80,7 @@ bool RemoteDebugDialog::validatePath() { QMessageBox msgBox; - QString path = getPath(); + QString path = getIpOrPath(); if (!QFileInfo(path).exists()) { msgBox.setText(tr("Path does not exist")); msgBox.exec(); @@ -93,36 +102,6 @@ bool RemoteDebugDialog::validatePort() 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() { } @@ -132,57 +111,137 @@ void RemoteDebugDialog::on_buttonBox_rejected() close(); } -void RemoteDebugDialog::setIp(QString ip) +void RemoteDebugDialog::removeItem() { - ui->ipEdit->setText(ip); - ui->ipEdit->selectAll(); + QListWidgetItem *item = ui->recentsIpListWidget->currentItem(); + + if (item == nullptr) + return; + + QVariant data = item->data(Qt::UserRole); + QString sitem = data.toString(); + + // Remove the item from recentIpList + QSettings settings; + QStringList ips = settings.value("recentIpList").toStringList(); + ips.removeAll(sitem); + settings.setValue("recentIpList", ips); + + // Also remove the line from list + ui->recentsIpListWidget->takeItem(ui->recentsIpListWidget->currentRow()); + checkIfEmpty(); } -void RemoteDebugDialog::setPath(QString path) +void RemoteDebugDialog::clearAll() { - ui->pathEdit->setText(path); - ui->pathEdit->selectAll(); + QSettings settings; + ui->recentsIpListWidget->clear(); + + QStringList ips = settings.value("recentIpList").toStringList(); + ips.clear(); + settings.setValue("recentIpList", ips); + + checkIfEmpty(); } -void RemoteDebugDialog::setPort(QString port) +void RemoteDebugDialog::fillFormData(QString formdata) { - ui->portEdit->setText(port); - ui->portEdit->selectAll(); + QString ipText = ""; + QString portText = ""; + const DbgBackend* backend = nullptr; + for (auto& back : dbgBackends) { + if (formdata.startsWith(back.prefix)) { + backend = &back; + } + } + if (!backend) { + return; + } + + if (backend->type == GDB) { + // Format is | prefix | IP | : | PORT | + int lastColon = formdata.lastIndexOf(QString(":")); + portText = formdata.mid(lastColon + 1, formdata.length()); + ipText = formdata.mid(backend->prefix.length(), lastColon - backend->prefix.length()); + } else if (backend->type == WINDBG) { + // Format is | prefix | PATH | + ipText = formdata.mid(backend->prefix.length()); + } + ui->debuggerCombo->setCurrentText(backend->name); + ui->ipEdit->setText(ipText); + ui->portEdit->setText(portText); } -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())); + int debugger = getDebugger(); + if (debugger == WINDBG) { + return QString("%1%2").arg(dbgBackends[WINDBG].prefix, getIpOrPath()); + } else if (debugger == GDB) { + return QString("%1%2:%3").arg(dbgBackends[GDB].prefix, getIpOrPath(), QString::number(getPort())); + } + return "- uri error"; +} + +bool RemoteDebugDialog::fillRecentIpList() +{ + QSettings settings; + + // Fetch recentIpList + QStringList ips = settings.value("recentIpList").toStringList(); + QMutableListIterator it(ips); + while (it.hasNext()) { + const QString ip = it.next(); + const QString text = QString("%1").arg(ip); + QListWidgetItem *item = new QListWidgetItem( + text + ); + item->setData(Qt::UserRole, ip); + // Fill recentsIpListWidget + ui->recentsIpListWidget->addItem(item); } - return NULL; + if (!ips.isEmpty()) { + fillFormData(ips[0]); + } + + checkIfEmpty(); + + return !ips.isEmpty(); } -QString RemoteDebugDialog::getIp() const +void RemoteDebugDialog::checkIfEmpty() +{ + QSettings settings; + QStringList ips = settings.value("recentIpList").toStringList(); + + if (ips.isEmpty()) { + ui->recentsIpListWidget->setVisible(false); + ui->line->setVisible(false); + } else { + // TODO: Find a way to make the list widget not to high + } +} + +void RemoteDebugDialog::itemClicked(QListWidgetItem *item) +{ + QVariant data = item->data(Qt::UserRole); + QString ipport = data.toString(); + fillFormData(ipport); +} + +QString RemoteDebugDialog::getIpOrPath() 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 +int RemoteDebugDialog::getDebugger() const { - return ui->debuggerCombo->currentText(); + return ui->debuggerCombo->currentIndex(); } diff --git a/src/dialogs/RemoteDebugDialog.h b/src/dialogs/RemoteDebugDialog.h index 8ac5019c..c04909b7 100644 --- a/src/dialogs/RemoteDebugDialog.h +++ b/src/dialogs/RemoteDebugDialog.h @@ -2,6 +2,7 @@ #define REMOTEDEBUGDIALOG_H #include +#include #include namespace Ui { @@ -19,30 +20,58 @@ public: explicit RemoteDebugDialog(QWidget *parent = nullptr); ~RemoteDebugDialog(); - void setIp(QString ip); - void setPort(QString port); - void setPath(QString path); - void setDebugger(QString debugger); + /** + * @brief Generate a URI for given UI input + */ 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(); + + /** + * @brief Clears the list of recent connections. + * Triggers when you right click and click on "Clear All" in remote debug dialog. + */ + void clearAll(); + + /** + * @brief Clears the selected item in the list of recent connections. + * Triggers when you right click and click on "Remove Item" in remote debug dialog. + */ + void removeItem(); + + /** + * @brief Fills the form with the selected item's data. + */ + void itemClicked(QListWidgetItem *item); private: - void activateGdb(); - void activateWinDbgPipe(); + std::unique_ptr ui; + int getPort() const; + int getDebugger() const; + QString getIpOrPath() const; + + bool validatePath(); bool validateIp(); bool validatePort(); - bool validatePath(); - std::unique_ptr ui; + /** + * @brief Fills recent remote connections. + */ + bool fillRecentIpList(); + + /** + * @brief Fills the remote debug dialog form from a given URI + * Eg: gdb://127.0.0.1:8754 or windbg:///tmp/abcd + */ + void fillFormData(QString formdata); + + /** + * @brief Checks if the recent connection list is empty or and hide/unhide the table view + */ + void checkIfEmpty(); }; #endif // REMOTE_DEBUG_DIALOG diff --git a/src/dialogs/RemoteDebugDialog.ui b/src/dialogs/RemoteDebugDialog.ui index 566f4760..d506081f 100644 --- a/src/dialogs/RemoteDebugDialog.ui +++ b/src/dialogs/RemoteDebugDialog.ui @@ -9,156 +9,171 @@ 0 0 - 320 - 170 + 373 + 204 + + + 0 + 0 + + Remote debugging configuration - - - - 10 - 10 - 300 - 110 - + + + QLayout::SetMinimumSize - - - - - Debugger: - - - - - - - - GDB + + + + QLayout::SetMinimumSize + + + + + + 0 + 0 + - - - WinDbg - Pipe + Debugger: - - - - - - - IP: - - - - - - - - 382 - 16777215 - - - - - - - - - - false - - - - - - - - - - Port: - - - - - - - - 382 - 16777215 - - - - - - - - - - false - - - - - - - - - - Path: - - - - - - - - 382 - 16777215 - - - - - - - - - - false - - - - - - - - - - - - 10 - 130 - 301 - 35 - + + + + + + + 382 + 16777215 + + + + + + + + + + false + + + 127.0.0.1 or /tmp/win.sock + + + + + + + + + + Port: + + + + + + + IP or Path: + + + + + + + + 382 + 16777215 + + + + + + + + + + false + + + Enter Port + + + + + + + + + QLayout::SetMinimumSize + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 0 + 60 + + + + Qt::ActionsContextMenu + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + Remove item - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - + + + + Remove all + + + Remove all + + + + debuggerCombo + ipEdit + portEdit + recentsIpListWidget + diff --git a/src/widgets/DebugActions.cpp b/src/widgets/DebugActions.cpp index 603f2070..99839e27 100644 --- a/src/widgets/DebugActions.cpp +++ b/src/widgets/DebugActions.cpp @@ -11,6 +11,7 @@ #include #include #include +#include DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QObject(main), @@ -107,7 +108,8 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : // Toggle all buttons except restart, suspend(=continue) and stop since those are // necessary to avoid staying stuck toggleActions = {actionStepOver, actionStep, actionStepOut, actionContinueUntilMain, - actionContinueUntilCall, actionContinueUntilSyscall}; + actionContinueUntilCall, actionContinueUntilSyscall + }; toggleConnectionActions = {actionAttach, actionStartRemote}; connect(Core(), &CutterCore::debugProcessFinished, this, [ = ](int pid) { @@ -176,7 +178,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : 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(), [=]() { + connect(actionContinue, &QAction::triggered, Core(), [ = ]() { // Switch between continue and suspend depending on the debugger's state if (Core()->isDebugTaskInProgress()) { Core()->suspendDebug(); @@ -207,7 +209,7 @@ void DebugActions::showDebugWarning() 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")); + tr("If you encounter any problems or have suggestions, please submit an issue to https://github.com/radareorg/cutter/issues")); msgBox.exec(); } } @@ -237,6 +239,12 @@ void DebugActions::onAttachedRemoteDebugger(bool successfully) msgBox.exec(); attachRemoteDialog(); } else { + QSettings settings; + QStringList ips = settings.value("recentIpList").toStringList(); + ips.removeAll(remoteDialog->getUri()); + ips.prepend(remoteDialog->getUri()); + settings.setValue("recentIpList", ips); + delete remoteDialog; remoteDialog = nullptr; attachRemoteDebugger(); @@ -352,7 +360,7 @@ void DebugActions::setAllActionsVisible(bool visible) void DebugActions::chooseThemeIcons() { // List of QActions which have alternative icons in different themes - const QList> kSupportedIconsNames { + const QList> kSupportedIconsNames { { actionStep, QStringLiteral("step_into.svg") }, { actionStepOver, QStringLiteral("step_over.svg") }, { actionStepOut, QStringLiteral("step_out.svg") }, @@ -363,7 +371,7 @@ void DebugActions::chooseThemeIcons() // Set the correct icon for the QAction - qhelpers::setThemeIcons(kSupportedIconsNames, [](void *obj, const QIcon &icon) { - static_cast(obj)->setIcon(icon); + qhelpers::setThemeIcons(kSupportedIconsNames, [](void *obj, const QIcon & icon) { + static_cast(obj)->setIcon(icon); }); }