diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 1f89c7615b1..109106f05c1 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -3016,6 +3016,11 @@ void DebuggerPluginPrivate::extensionsInitialized() mstart->addAction(cmd, CC::G_DEFAULT_ONE); } + QAction *sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, "Debugger.Sep.Start", globalcontext); + mstart->addAction(cmd); + cmd = am->registerAction(m_detachAction, "Debugger.Detach", globalcontext); cmd->setAttribute(Command::CA_Hide); @@ -3051,7 +3056,7 @@ void DebuggerPluginPrivate::extensionsInitialized() cmd->setDefaultText(tr("Reset Debugger")); debugMenu->addAction(cmd, CC::G_DEFAULT_ONE); - QAction *sep = new QAction(this); + sep = new QAction(this); sep->setSeparator(true); cmd = am->registerAction(sep, "Debugger.Sep.Step", globalcontext); debugMenu->addAction(cmd); diff --git a/src/plugins/remotelinux/remotelinux.pro b/src/plugins/remotelinux/remotelinux.pro index a616927a1e3..49df34ba879 100644 --- a/src/plugins/remotelinux/remotelinux.pro +++ b/src/plugins/remotelinux/remotelinux.pro @@ -54,7 +54,8 @@ HEADERS += \ remotelinuxutils.h \ deploymentsettingsassistant.h \ remotelinuxdeployconfigurationwidget.h \ - profilesupdatedialog.h + profilesupdatedialog.h \ + startgdbserverdialog.h SOURCES += \ remotelinuxplugin.cpp \ @@ -103,7 +104,8 @@ SOURCES += \ remotelinuxutils.cpp \ deploymentsettingsassistant.cpp \ remotelinuxdeployconfigurationwidget.cpp \ - profilesupdatedialog.cpp + profilesupdatedialog.cpp \ + startgdbserverdialog.cpp FORMS += \ linuxdevicefactoryselectiondialog.ui \ @@ -113,7 +115,8 @@ FORMS += \ linuxdeviceconfigurationssettingswidget.ui \ sshkeycreationdialog.ui \ remotelinuxdeployconfigurationwidget.ui \ - profilesupdatedialog.ui + profilesupdatedialog.ui \ + startgdbserverdialog.ui DEFINES += QT_NO_CAST_TO_ASCII DEFINES += REMOTELINUX_LIBRARY diff --git a/src/plugins/remotelinux/remotelinuxplugin.cpp b/src/plugins/remotelinux/remotelinuxplugin.cpp index f821834fa2e..aa47157a60a 100644 --- a/src/plugins/remotelinux/remotelinuxplugin.cpp +++ b/src/plugins/remotelinux/remotelinuxplugin.cpp @@ -41,8 +41,17 @@ #include "remotelinuxrunconfigurationfactory.h" #include "remotelinuxruncontrolfactory.h" #include "remotelinuxsettingspages.h" +#include "startgdbserverdialog.h" + +#include +#include +#include +#include + +#include #include +#include namespace RemoteLinux { namespace Internal { @@ -51,10 +60,6 @@ RemoteLinuxPlugin::RemoteLinuxPlugin() { } -RemoteLinuxPlugin::~RemoteLinuxPlugin() -{ -} - bool RemoteLinuxPlugin::initialize(const QStringList &arguments, QString *errorMessage) { @@ -77,6 +82,28 @@ bool RemoteLinuxPlugin::initialize(const QStringList &arguments, void RemoteLinuxPlugin::extensionsInitialized() { + using namespace Core; + ICore *core = ICore::instance(); + ActionManager *am = core->actionManager(); + ActionContainer *mstart = am->actionContainer(ProjectExplorer::Constants::M_DEBUG_STARTDEBUGGING); + + const Context globalcontext(Core::Constants::C_GLOBAL); + + QAction *startGdbServerAction = new QAction(tr("Start Remote Debug Server"), 0); + Command *cmd = am->registerAction(startGdbServerAction, "StartGdbServer", globalcontext); + cmd->setDefaultText(tr("Start Gdbserver")); + mstart->addAction(cmd, Constants::G_DEFAULT_TWO); + + connect(startGdbServerAction, SIGNAL(triggered()), SLOT(startGdbServer())); +} + +void RemoteLinuxPlugin::startGdbServer() +{ + StartGdbServerDialog dlg; + int result = dlg.exec(); + if (result == QDialog::Rejected) + return; + dlg.startGdbServer(); } } // namespace Internal diff --git a/src/plugins/remotelinux/remotelinuxplugin.h b/src/plugins/remotelinux/remotelinuxplugin.h index d3620f145a6..93fe64ae664 100644 --- a/src/plugins/remotelinux/remotelinuxplugin.h +++ b/src/plugins/remotelinux/remotelinuxplugin.h @@ -41,12 +41,15 @@ namespace Internal { class RemoteLinuxPlugin : public ExtensionSystem::IPlugin { Q_OBJECT + public: RemoteLinuxPlugin(); - ~RemoteLinuxPlugin(); bool initialize(const QStringList &arguments, QString *errorMessage); void extensionsInitialized(); + +private slots: + void startGdbServer(); }; } // namespace Internal diff --git a/src/plugins/remotelinux/remotelinuxprocesslist.cpp b/src/plugins/remotelinux/remotelinuxprocesslist.cpp index f156acb9ecc..139a2dc83c7 100644 --- a/src/plugins/remotelinux/remotelinuxprocesslist.cpp +++ b/src/plugins/remotelinux/remotelinuxprocesslist.cpp @@ -105,6 +105,11 @@ void AbstractRemoteLinuxProcessList::killProcess(int row) startProcess(killProcessCommandLine(d->remoteProcesses.at(row))); } +int AbstractRemoteLinuxProcessList::pidAt(int row) const +{ + return d->remoteProcesses.at(row).pid; +} + int AbstractRemoteLinuxProcessList::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : d->remoteProcesses.count(); diff --git a/src/plugins/remotelinux/remotelinuxprocesslist.h b/src/plugins/remotelinux/remotelinuxprocesslist.h index 020858ed96e..327b1abf036 100644 --- a/src/plugins/remotelinux/remotelinuxprocesslist.h +++ b/src/plugins/remotelinux/remotelinuxprocesslist.h @@ -53,6 +53,7 @@ public: void update(); void killProcess(int row); + int pidAt(int row) const; signals: void error(const QString &errorMsg); diff --git a/src/plugins/remotelinux/startgdbserverdialog.cpp b/src/plugins/remotelinux/startgdbserverdialog.cpp new file mode 100644 index 00000000000..d4581de1dd3 --- /dev/null +++ b/src/plugins/remotelinux/startgdbserverdialog.cpp @@ -0,0 +1,229 @@ +#include "startgdbserverdialog.h" +#include "ui_startgdbserverdialog.h" + +#include "remotelinuxprocesslist.h" +#include "linuxdeviceconfiguration.h" +#include "linuxdeviceconfigurations.h" +#include "remotelinuxusedportsgatherer.h" +#include "portlist.h" + +#include +#include +#include + +#include +#include +#include + +namespace RemoteLinux { +namespace Internal { + +class StartGdbServerDialogPrivate +{ +public: + StartGdbServerDialogPrivate() + : processList(0) + {} + + + LinuxDeviceConfiguration::ConstPtr currentDevice() const + { + LinuxDeviceConfigurations *devices = LinuxDeviceConfigurations::instance(); + return devices->deviceAt(ui.deviceComboBox->currentIndex()); + } + + AbstractRemoteLinuxProcessList *processList; + QSortFilterProxyModel proxyModel; + Ui::StartGdbServerDialog ui; + RemoteLinuxUsedPortsGatherer gatherer; + Utils::SshRemoteProcessRunner::Ptr runner; +}; + +} // namespace Internal + +StartGdbServerDialog::StartGdbServerDialog(QWidget *parent) : + QDialog(parent), + d(new Internal::StartGdbServerDialogPrivate) +{ + LinuxDeviceConfigurations *devices = LinuxDeviceConfigurations::instance(); + d->ui.setupUi(this); + d->ui.deviceComboBox->setModel(devices); + connect(&d->gatherer, SIGNAL(error(QString)), SLOT(portGathererError(QString))); + connect(&d->gatherer, SIGNAL(portListReady()), SLOT(portListReady())); + if (devices->rowCount() == 0) { + d->ui.tableView->setEnabled(false); + } else { + d->ui.tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + d->proxyModel.setDynamicSortFilter(true); + d->proxyModel.setFilterKeyColumn(1); + d->ui.tableView->setModel(&d->proxyModel); + connect(d->ui.processFilterLineEdit, SIGNAL(textChanged(QString)), + &d->proxyModel, SLOT(setFilterRegExp(QString))); + + connect(d->ui.tableView->selectionModel(), + SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + SLOT(handleSelectionChanged())); + connect(d->ui.updateListButton, SIGNAL(clicked()), + SLOT(updateProcessList())); + connect(d->ui.attachProcessButton, SIGNAL(clicked()), SLOT(attachToProcess())); + connect(&d->proxyModel, SIGNAL(layoutChanged()), + SLOT(handleProcessListUpdated())); + connect(d->ui.deviceComboBox, SIGNAL(currentIndexChanged(int)), + SLOT(attachToDevice(int))); + handleSelectionChanged(); + attachToDevice(0); + } +} + +StartGdbServerDialog::~StartGdbServerDialog() +{ + delete d->processList; + delete d; +} + + +void StartGdbServerDialog::attachToDevice(int index) +{ + LinuxDeviceConfigurations *devices = LinuxDeviceConfigurations::instance(); + delete d->processList; + d->processList = new GenericRemoteLinuxProcessList(devices->deviceAt(index)); + d->proxyModel.setSourceModel(d->processList); + connect(d->processList, SIGNAL(error(QString)), + SLOT(handleRemoteError(QString))); + connect(d->processList, SIGNAL(modelReset()), + SLOT(handleProcessListUpdated())); + connect(d->processList, SIGNAL(processKilled()), + SLOT(handleProcessKilled()), Qt::QueuedConnection); + updateProcessList(); +} + +void StartGdbServerDialog::handleRemoteError(const QString &errorMsg) +{ + QMessageBox::critical(this, tr("Remote Error"), errorMsg); + d->ui.updateListButton->setEnabled(true); + handleSelectionChanged(); +} + +void StartGdbServerDialog::handleProcessListUpdated() +{ + d->ui.updateListButton->setEnabled(true); + d->ui.tableView->resizeRowsToContents(); + handleSelectionChanged(); +} + +void StartGdbServerDialog::updateProcessList() +{ + d->ui.updateListButton->setEnabled(false); + d->ui.attachProcessButton->setEnabled(false); + d->processList->update(); +} + +void StartGdbServerDialog::attachToProcess() +{ + const QModelIndexList &indexes = + d->ui.tableView->selectionModel()->selectedIndexes(); + if (indexes.empty()) + return; + d->ui.updateListButton->setEnabled(false); + d->ui.attachProcessButton->setEnabled(false); + + LinuxDeviceConfiguration::ConstPtr device = d->currentDevice(); + PortList ports = device->freePorts(); + const int port = d->gatherer.getNextFreePort(&ports); + const int row = d->proxyModel.mapToSource(indexes.first()).row(); + QTC_ASSERT(row >= 0, processAborted(); return); + const int pid = d->processList->pidAt(row); + if (port == -1) { + emit processAborted(); + } else { + emit pidSelected(pid); + emit portSelected(pid); + startGdbServerOnPort(port, pid); + } +} + +void StartGdbServerDialog::handleProcessKilled() +{ + updateProcessList(); +} + +void StartGdbServerDialog::handleSelectionChanged() +{ + d->ui.attachProcessButton->setEnabled(d->ui.tableView->selectionModel()->hasSelection()); +} + +void StartGdbServerDialog::portGathererError(const QString &text) +{ + d->ui.textBrowser->append(tr("Could not retrieve list of free ports:")); + d->ui.textBrowser->append(text); + emit processAborted(); +} + +void StartGdbServerDialog::portListReady() +{ + d->ui.updateListButton->setEnabled(true); + d->ui.attachProcessButton->setEnabled(true); +} + +void StartGdbServerDialog::startGdbServer() +{ + LinuxDeviceConfiguration::ConstPtr device = d->currentDevice(); + d->gatherer.start(Utils::SshConnection::create(device->sshParameters()), device); +} + +void StartGdbServerDialog::handleConnectionError() +{ + d->ui.textBrowser->append(tr("Connection error: %1") + .arg(d->runner->connection()->errorString())); + emit processAborted(); +} + +void StartGdbServerDialog::handleProcessStarted() +{ + d->ui.textBrowser->append(tr("Starting gdbserver...")); +} + +void StartGdbServerDialog::handleProcessOutputAvailable(const QByteArray &ba) +{ + d->ui.textBrowser->append(QString::fromUtf8(ba.trimmed())); +} + +void StartGdbServerDialog::handleProcessErrorOutput(const QByteArray &ba) +{ + d->ui.textBrowser->append(QString::fromUtf8(ba.trimmed())); + // "Attached; pid = 16740" + // "Listening on port 10000" + int pos = ba.indexOf("Listening on port"); + if (pos != -1) { + int port = ba.mid(pos + 18).toInt(); + d->ui.textBrowser->append(tr("Port %1 is now accessible.").arg(port)); + emit portOpened(port); + } +} + +void StartGdbServerDialog::handleProcessClosed(int status) +{ + d->ui.textBrowser->append(tr("Process gdbserver finished. Status: %1").arg(status)); +} + +void StartGdbServerDialog::startGdbServerOnPort(int port, int pid) +{ + LinuxDeviceConfiguration::ConstPtr device = d->currentDevice(); + d->runner = Utils::SshRemoteProcessRunner::create(device->sshParameters()); + connect(d->runner.data(), SIGNAL(connectionError(Utils::SshError)), + SLOT(handleConnectionError())); + connect(d->runner.data(), SIGNAL(processStarted()), + SLOT(handleProcessStarted())); + connect(d->runner.data(), SIGNAL(processOutputAvailable(QByteArray)), + SLOT(handleProcessOutputAvailable(QByteArray))); + connect(d->runner.data(), SIGNAL(processErrorOutputAvailable(QByteArray)), + SLOT(handleProcessErrorOutput(QByteArray))); + connect(d->runner.data(), SIGNAL(processClosed(int)), + SLOT(handleProcessClosed(int))); + + QByteArray cmd = "/usr/bin/gdbserver --attach localhost:" + + QByteArray::number(port) + " " + QByteArray::number(pid); + d->runner->run(cmd); +} + +} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/startgdbserverdialog.h b/src/plugins/remotelinux/startgdbserverdialog.h new file mode 100644 index 00000000000..ba7e32b086b --- /dev/null +++ b/src/plugins/remotelinux/startgdbserverdialog.h @@ -0,0 +1,54 @@ +#ifndef STARTGDBSERVERDIALOG_H +#define STARTGDBSERVERDIALOG_H + +#include "remotelinux_export.h" + +#include + +namespace RemoteLinux { + +namespace Internal { +class StartGdbServerDialogPrivate; +} // namespace Internal + +class REMOTELINUX_EXPORT StartGdbServerDialog : public QDialog +{ + Q_OBJECT + +public: + explicit StartGdbServerDialog(QWidget *parent = 0); + ~StartGdbServerDialog(); + + void startGdbServer(); + +signals: + void pidSelected(int pid); + void portSelected(int port); + void portOpened(int port); + void processAborted(); + +private slots: + void attachToDevice(int index); + void handleRemoteError(const QString &errorMessage); + void handleProcessListUpdated(); + void updateProcessList(); + void attachToProcess(); + void handleProcessKilled(); + void handleSelectionChanged(); + void portGathererError(const QString &errorMessage); + void portListReady(); + + void handleProcessClosed(int); + void handleProcessErrorOutput(const QByteArray &data); + void handleProcessOutputAvailable(const QByteArray &data); + void handleProcessStarted(); + void handleConnectionError(); + +private: + void startGdbServerOnPort(int port, int pid); + Internal::StartGdbServerDialogPrivate *d; +}; + +} // namespace RemoteLinux + +#endif // STARTGDBSERVERDIALOG_H diff --git a/src/plugins/remotelinux/startgdbserverdialog.ui b/src/plugins/remotelinux/startgdbserverdialog.ui new file mode 100644 index 00000000000..e46116c5bcb --- /dev/null +++ b/src/plugins/remotelinux/startgdbserverdialog.ui @@ -0,0 +1,190 @@ + + + StartGdbServerDialog + + + + 0 + 0 + 420 + 564 + + + + List of Remote Processes + + + + + + + + Device: + + + + + + + + + + &Filter by process name: + + + processFilterLineEdit + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + false + + + true + + + 100 + + + true + + + false + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + &Attach to Selected Process + + + + + + + &Update List + + + + + + + Qt::Horizontal + + + + 242 + 20 + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + buttonBox + filterLabel + processFilterLineEdit + deviceComboBox + label + updateListButton + attachProcessButton + textBrowser + + + + + buttonBox + accepted() + StartGdbServerDialog + accept() + + + 257 + 290 + + + 157 + 274 + + + + + buttonBox + rejected() + StartGdbServerDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +