Remote Debug dialog improvements (#2472)

Co-authored-by: nk521 <nk_mason@protonmail.com>
This commit is contained in:
xarkes 2020-11-18 13:15:36 +01:00 committed by GitHub
parent 6aa13cd394
commit e236f6b0fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 359 additions and 248 deletions

View File

@ -4,12 +4,23 @@
#include <QHostAddress>
#include <QFileInfo>
#include <QMessageBox>
#include <QSettings>
#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<DbgBackend> 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<void (QComboBox::*)(int)>(&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<QString> 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();
}

View File

@ -2,6 +2,7 @@
#define REMOTEDEBUGDIALOG_H
#include <QDialog>
#include <QListWidgetItem>
#include <memory>
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::RemoteDebugDialog> ui;
int getPort() const;
int getDebugger() const;
QString getIpOrPath() const;
bool validatePath();
bool validateIp();
bool validatePort();
bool validatePath();
std::unique_ptr<Ui::RemoteDebugDialog> 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

View File

@ -9,156 +9,171 @@
<rect>
<x>0</x>
<y>0</y>
<width>320</width>
<height>170</height>
<width>373</width>
<height>204</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</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>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</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>
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="debugText">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</item>
<item>
<property name="text">
<string>WinDbg - Pipe</string>
<string>Debugger:</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>
</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">127.0.0.1 or /tmp/win.sock</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="debuggerCombo"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="portText">
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="ipText">
<property name="text">
<string>IP or Path:</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">Enter Port</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="recentsIpListWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>60</height>
</size>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="selectionRectVisible">
<bool>true</bool>
</property>
</widget>
</item>
<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>
</item>
</layout>
<action name="actionRemoveItem">
<property name="text">
<string>Remove item</string>
</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>
</action>
<action name="actionRemoveAll">
<property name="text">
<string>Remove all</string>
</property>
<property name="toolTip">
<string>Remove all</string>
</property>
</action>
</widget>
<tabstops>
<tabstop>debuggerCombo</tabstop>
<tabstop>ipEdit</tabstop>
<tabstop>portEdit</tabstop>
<tabstop>recentsIpListWidget</tabstop>
</tabstops>
<resources/>
<connections>
<connection>

View File

@ -11,6 +11,7 @@
#include <QFileInfo>
#include <QToolBar>
#include <QToolButton>
#include <QSettings>
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<QPair<void*, QString>> kSupportedIconsNames {
const QList<QPair<void *, QString>> 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<QAction*>(obj)->setIcon(icon);
qhelpers::setThemeIcons(kSupportedIconsNames, [](void *obj, const QIcon & icon) {
static_cast<QAction *>(obj)->setIcon(icon);
});
}