debugger: merge attach to local and remote process dialogs

This also merges a larger part of the two code paths.

Change-Id: I84a88c53ebc0073becac88ba04e63efd9a4a98b3
Reviewed-by: Thomas Hartmann <Thomas.Hartmann@nokia.com>
Reviewed-by: hjk <qthjk@ovi.com>
This commit is contained in:
hjk
2012-08-08 12:03:07 +02:00
parent 389dc900cc
commit 7a6beafa69
14 changed files with 495 additions and 931 deletions

View File

@@ -41,7 +41,6 @@
#include <projectexplorer/abi.h> #include <projectexplorer/abi.h>
#include <projectexplorer/profilechooser.h> #include <projectexplorer/profilechooser.h>
#include <projectexplorer/profileinformation.h> #include <projectexplorer/profileinformation.h>
#include <utils/filterlineedit.h>
#include <utils/historycompleter.h> #include <utils/historycompleter.h>
#include <utils/pathchooser.h> #include <utils/pathchooser.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -51,7 +50,6 @@
#include <QApplication> #include <QApplication>
#include <QButtonGroup> #include <QButtonGroup>
#include <QCheckBox> #include <QCheckBox>
#include <QCoreApplication>
#include <QDebug> #include <QDebug>
#include <QDialog> #include <QDialog>
#include <QDialogButtonBox> #include <QDialogButtonBox>
@@ -68,7 +66,6 @@
#include <QRadioButton> #include <QRadioButton>
#include <QRegExp> #include <QRegExp>
#include <QScrollArea> #include <QScrollArea>
#include <QSortFilterProxyModel>
#include <QSpinBox> #include <QSpinBox>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QTreeView> #include <QTreeView>
@@ -79,301 +76,15 @@ using namespace Core;
using namespace ProjectExplorer; using namespace ProjectExplorer;
using namespace Utils; using namespace Utils;
namespace Debugger {
namespace Internal {
bool operator<(const ProcData &p1, const ProcData &p2)
{
return p1.name < p2.name;
}
// A filterable process list model
class ProcessListFilterModel : public QSortFilterProxyModel
{
public:
explicit ProcessListFilterModel(QObject *parent);
QString processIdAt(const QModelIndex &index) const;
QString executableForPid(const QString& pid) const;
void populate(QList<ProcData> processes, const QString &excludePid);
private:
enum { ProcessImageRole = Qt::UserRole, ProcessNameRole };
bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
QStandardItemModel *m_model;
};
ProcessListFilterModel::ProcessListFilterModel(QObject *parent)
: QSortFilterProxyModel(parent),
m_model(new QStandardItemModel(this))
{
QStringList columns;
columns << AttachExternalDialog::tr("Process ID")
<< AttachExternalDialog::tr("Name")
<< AttachExternalDialog::tr("State");
m_model->setHorizontalHeaderLabels(columns);
setSourceModel(m_model);
setFilterCaseSensitivity(Qt::CaseInsensitive);
setFilterKeyColumn(1);
}
bool ProcessListFilterModel::lessThan(const QModelIndex &left,
const QModelIndex &right) const
{
const QString l = sourceModel()->data(left).toString();
const QString r = sourceModel()->data(right).toString();
if (left.column() == 0)
return l.toInt() < r.toInt();
return l < r;
}
QString ProcessListFilterModel::processIdAt(const QModelIndex &index) const
{
if (index.isValid()) {
const QModelIndex index0 = mapToSource(index);
QModelIndex siblingIndex = index0.sibling(index0.row(), 0);
if (const QStandardItem *item = m_model->itemFromIndex(siblingIndex))
return item->text();
}
return QString();
}
QString ProcessListFilterModel::executableForPid(const QString &pid) const
{
const int rowCount = m_model->rowCount();
for (int r = 0; r < rowCount; r++) {
const QStandardItem *item = m_model->item(r, 0);
if (item->text() == pid) {
QString name = item->data(ProcessImageRole).toString();
if (name.isEmpty())
name = item->data(ProcessNameRole).toString();
return name;
}
}
return QString();
}
void ProcessListFilterModel::populate
(QList<ProcData> processes, const QString &excludePid)
{
qStableSort(processes);
if (const int rowCount = m_model->rowCount())
m_model->removeRows(0, rowCount);
QStandardItem *root = m_model->invisibleRootItem();
foreach (const ProcData &proc, processes) {
QList<QStandardItem *> row;
row.append(new QStandardItem(proc.ppid));
QString name = proc.image.isEmpty() ? proc.name : proc.image;
row.back()->setData(name, ProcessImageRole);
row.append(new QStandardItem(proc.name));
row.back()->setToolTip(proc.image);
row.append(new QStandardItem(proc.state));
if (proc.ppid == excludePid)
foreach (QStandardItem *item, row)
item->setEnabled(false);
root->appendRow(row);
}
}
///////////////////////////////////////////////////////////////////////
//
// AttachExternalDialog
//
///////////////////////////////////////////////////////////////////////
class AttachExternalDialogPrivate
{
public:
QLineEdit *pidLineEdit;
FilterLineEdit *filterWidget;
ProfileChooser *profileComboBox;
QTreeView *procView;
QDialogButtonBox *buttonBox;
QString selfPid;
ProcessListFilterModel *model;
};
AttachExternalDialog::AttachExternalDialog(QWidget *parent)
: QDialog(parent),
d(new AttachExternalDialogPrivate)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setWindowTitle(tr("Start Debugger"));
setMinimumHeight(500);
d->selfPid = QString::number(QCoreApplication::applicationPid());
d->model = new ProcessListFilterModel(this);
d->pidLineEdit = new QLineEdit(this);
d->filterWidget = new FilterLineEdit(this);
d->filterWidget->setFocus(Qt::TabFocusReason);
d->profileComboBox = new ProfileChooser(this, ProfileChooser::LocalDebugging);
d->procView = new QTreeView(this);
d->procView->setAlternatingRowColors(true);
d->procView->setRootIsDecorated(false);
d->procView->setUniformRowHeights(true);
d->procView->setEditTriggers(QAbstractItemView::NoEditTriggers);
d->procView->setModel(d->model);
d->procView->setSortingEnabled(true);
d->procView->sortByColumn(1, Qt::AscendingOrder);
QPushButton *refreshButton = new QPushButton(tr("Refresh"));
d->buttonBox = new QDialogButtonBox(this);
d->buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
d->buttonBox->addButton(refreshButton, QDialogButtonBox::ActionRole);
QFrame *line = new QFrame(this);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
QFormLayout *formLayout = new QFormLayout();
formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
formLayout->addRow(tr("Attach to &process ID:"), d->pidLineEdit);
formLayout->addRow(tr("&Target:"), d->profileComboBox);
formLayout->addRow(d->filterWidget);
QVBoxLayout *vboxLayout = new QVBoxLayout(this);
vboxLayout->addLayout(formLayout);
vboxLayout->addWidget(d->procView);
vboxLayout->addWidget(line);
vboxLayout->addWidget(d->buttonBox);
okButton()->setDefault(true);
okButton()->setEnabled(false);
connect(refreshButton, SIGNAL(clicked()), SLOT(rebuildProcessList()));
connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept()));
connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject()));
// Do not use activated, will be single click in Oxygen
connect(d->procView, SIGNAL(doubleClicked(QModelIndex)),
SLOT(procSelected(QModelIndex)));
connect(d->procView, SIGNAL(clicked(QModelIndex)),
SLOT(procClicked(QModelIndex)));
connect(d->pidLineEdit, SIGNAL(textChanged(QString)),
SLOT(pidChanged(QString)));
connect(d->filterWidget, SIGNAL(filterChanged(QString)),
SLOT(setFilterString(QString)));
rebuildProcessList();
}
AttachExternalDialog::~AttachExternalDialog()
{
delete d;
}
void AttachExternalDialog::setFilterString(const QString &filter)
{
d->model->setFilterFixedString(filter);
// Activate the line edit if there's a unique filtered process.
QString processId;
if (d->model->rowCount(QModelIndex()) == 1)
processId = d->model->processIdAt(d->model->index(0, 0, QModelIndex()));
d->pidLineEdit->setText(processId);
pidChanged(processId);
}
QPushButton *AttachExternalDialog::okButton() const
{
return d->buttonBox->button(QDialogButtonBox::Ok);
}
void AttachExternalDialog::rebuildProcessList()
{
d->model->populate(hostProcessList(), d->selfPid);
d->procView->expandAll();
d->procView->resizeColumnToContents(0);
d->procView->resizeColumnToContents(1);
}
void AttachExternalDialog::procSelected(const QModelIndex &proxyIndex)
{
const QString processId = d->model->processIdAt(proxyIndex);
if (!processId.isEmpty()) {
d->pidLineEdit->setText(processId);
if (okButton()->isEnabled())
okButton()->animateClick();
}
}
void AttachExternalDialog::procClicked(const QModelIndex &proxyIndex)
{
const QString processId = d->model->processIdAt(proxyIndex);
if (!processId.isEmpty())
d->pidLineEdit->setText(processId);
}
QString AttachExternalDialog::attachPIDText() const
{
return d->pidLineEdit->text().trimmed();
}
qint64 AttachExternalDialog::attachPID() const
{
return attachPIDText().toLongLong();
}
QString AttachExternalDialog::executable() const
{
// Search pid in model in case the user typed in the PID.
return d->model->executableForPid(attachPIDText());
}
Id AttachExternalDialog::profileId() const
{
return d->profileComboBox->currentProfileId();
}
void AttachExternalDialog::setProfileIndex(int i)
{
if (i >= 0 && i < d->profileComboBox->count())
d->profileComboBox->setCurrentIndex(i);
}
int AttachExternalDialog::profileIndex() const
{
return d->profileComboBox->currentIndex();
}
void AttachExternalDialog::pidChanged(const QString &pid)
{
const bool enabled = !pid.isEmpty() && pid != QLatin1String("0") && pid != d->selfPid
&& d->profileComboBox->currentIndex() >= 0;
okButton()->setEnabled(enabled);
}
void AttachExternalDialog::accept()
{
#ifdef Q_OS_WIN
const qint64 pid = attachPID();
if (pid && isWinProcessBeingDebugged(pid)) {
QMessageBox::warning(this, tr("Process Already Under Debugger Control"),
tr("The process %1 is already under the control of a debugger.\n"
"Qt Creator cannot attach to it.").arg(pid));
return;
}
#endif
QDialog::accept();
}
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// //
// StartExternalDialog // StartExternalDialog
// //
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
namespace Debugger {
namespace Internal {
class StartExternalParameters class StartExternalParameters
{ {
public: public:

View File

@@ -52,7 +52,6 @@ class DebuggerStartParameters;
namespace Internal { namespace Internal {
class AttachCoreDialogPrivate; class AttachCoreDialogPrivate;
class AttachExternalDialogPrivate;
class AttachToQmlPortDialogPrivate; class AttachToQmlPortDialogPrivate;
class ProcessListFilterModel; class ProcessListFilterModel;
class StartExternalDialogPrivate; class StartExternalDialogPrivate;
@@ -61,37 +60,6 @@ class StartRemoteDialogPrivate;
class StartRemoteEngineDialogPrivate; class StartRemoteEngineDialogPrivate;
class StartRemoteParameters; class StartRemoteParameters;
class AttachExternalDialog : public QDialog
{
Q_OBJECT
public:
explicit AttachExternalDialog(QWidget *parent);
~AttachExternalDialog();
qint64 attachPID() const;
QString executable() const;
int profileIndex() const;
void setProfileIndex(int);
Core::Id profileId() const;
void accept();
private slots:
void rebuildProcessList();
void procSelected(const QModelIndex &index);
void procClicked(const QModelIndex &index);
void pidChanged(const QString &index);
void setFilterString(const QString &filter);
private:
inline QPushButton *okButton() const;
inline QString attachPIDText() const;
AttachExternalDialogPrivate *d;
};
class StartExternalDialog : public QDialog class StartExternalDialog : public QDialog
{ {
Q_OBJECT Q_OBJECT

View File

@@ -63,6 +63,7 @@
#include "debuggertooltipmanager.h" #include "debuggertooltipmanager.h"
#include "localsandexpressionswindow.h" #include "localsandexpressionswindow.h"
#include "loadcoredialog.h" #include "loadcoredialog.h"
#include "hostutils.h"
#include "snapshothandler.h" #include "snapshothandler.h"
#include "threadshandler.h" #include "threadshandler.h"
@@ -94,11 +95,13 @@
#include <projectexplorer/abi.h> #include <projectexplorer/abi.h>
#include <projectexplorer/applicationrunconfiguration.h> #include <projectexplorer/applicationrunconfiguration.h>
#include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/devicesupport/deviceprocessesdialog.h>
#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorersettings.h> #include <projectexplorer/projectexplorersettings.h>
#include <projectexplorer/project.h> #include <projectexplorer/project.h>
#include <projectexplorer/session.h> #include <projectexplorer/session.h>
#include <projectexplorer/profilechooser.h>
#include <projectexplorer/profileinformation.h> #include <projectexplorer/profileinformation.h>
#include <projectexplorer/profilemanager.h> #include <projectexplorer/profilemanager.h>
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
@@ -776,13 +779,12 @@ public slots:
void startRemoteCdbSession(); void startRemoteCdbSession();
void startRemoteProcess(); void startRemoteProcess();
void startRemoteServer(); void startRemoteServer();
//bool queryRemoteParameters(DebuggerStartParameters &sp, bool useScript);
void attachToRemoteServer(); void attachToRemoteServer();
void attachToProcess(bool startServerOnly);
void attachToRunningApplication(); void attachToRunningApplication();
void attachToQmlPort(); void attachToQmlPort();
void startRemoteEngine(); void startRemoteEngine();
void attachExternalApplication(); //Q_SLOT void attachToLocalProcess(ProjectExplorer::RunControl *rc);
Q_SLOT void attachExternalApplication(ProjectExplorer::RunControl *rc);
void runScheduled(); void runScheduled();
void attachCore(); void attachCore();
@@ -1122,7 +1124,6 @@ public:
QAction *m_attachToRemoteServerAction; QAction *m_attachToRemoteServerAction;
QAction *m_startRemoteCdbAction; QAction *m_startRemoteCdbAction;
QAction *m_startRemoteLldbAction; QAction *m_startRemoteLldbAction;
QAction *m_attachToLocalProcessAction;
QAction *m_attachToCoreAction; QAction *m_attachToCoreAction;
QAction *m_detachAction; QAction *m_detachAction;
QAction *m_continueAction; QAction *m_continueAction;
@@ -1245,7 +1246,6 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(DebuggerPlugin *plugin) :
m_attachToQmlPortAction = 0; m_attachToQmlPortAction = 0;
m_startRemoteCdbAction = 0; m_startRemoteCdbAction = 0;
m_startRemoteLldbAction = 0; m_startRemoteLldbAction = 0;
m_attachToLocalProcessAction = 0;
m_attachToCoreAction = 0; m_attachToCoreAction = 0;
m_detachAction = 0; m_detachAction = 0;
@@ -1520,44 +1520,44 @@ void DebuggerPluginPrivate::startExternalApplication()
startDebugger(rc); startDebugger(rc);
} }
void DebuggerPluginPrivate::attachExternalApplication() //void DebuggerPluginPrivate::attachToLocalProcessHelper()
{ //{
AttachExternalDialog dlg(mainWindow()); // AttachExternalDialog dlg(mainWindow());
dlg.setProfileIndex(configValue(_("LastAttachExternalProfileIndex")).toInt()); // dlg.setProfileIndex(configValue(_("LastAttachExternalProfileIndex")).toInt());
if (dlg.exec() != QDialog::Accepted) // if (dlg.exec() != QDialog::Accepted)
return; // return;
if (dlg.attachPID() == 0) { // if (dlg.attachPID() == 0) {
QMessageBox::warning(mainWindow(), tr("Warning"), // QMessageBox::warning(mainWindow(), tr("Warning"),
tr("Cannot attach to process with PID 0")); // tr("Cannot attach to process with PID 0"));
return; // return;
} // }
setConfigValue(_("LastAttachExternalProfileIndex"), QVariant(dlg.profileIndex())); // setConfigValue(_("LastAttachExternalProfileIndex"), QVariant(dlg.profileIndex()));
DebuggerStartParameters sp; // DebuggerStartParameters sp;
fillParameters(&sp, dlg.profileId()); // fillParameters(&sp, dlg.profileId());
sp.attachPID = dlg.attachPID(); // sp.attachPID = dlg.attachPID();
sp.displayName = tr("Process %1").arg(dlg.attachPID()); // sp.displayName = tr("Process %1").arg(dlg.attachPID());
sp.executable = dlg.executable(); // sp.executable = dlg.executable();
sp.startMode = AttachExternal; // sp.startMode = AttachExternal;
sp.closeMode = DetachAtClose; // sp.closeMode = DetachAtClose;
if (DebuggerRunControl *rc = createDebugger(sp)) // if (DebuggerRunControl *rc = createDebugger(sp))
startDebugger(rc); // startDebugger(rc);
} //}
void DebuggerPluginPrivate::attachExternalApplication(RunControl *rc) //void DebuggerPluginPrivate::attachToLocalProcess(RunControl *rc)
{ //{
DebuggerStartParameters sp; // DebuggerStartParameters sp;
fillParameters(&sp, ProfileManager::instance()->defaultProfile()->id()); // FIXME: Extract from rc. // fillParameters(&sp, ProfileManager::instance()->defaultProfile()->id()); // FIXME: Extract from rc.
sp.attachPID = rc->applicationProcessHandle().pid(); // sp.attachPID = rc->applicationProcessHandle().pid();
sp.displayName = tr("Debugger attached to %1").arg(rc->displayName()); // sp.displayName = tr("Debugger attached to %1").arg(rc->displayName());
sp.startMode = AttachExternal; // sp.startMode = AttachExternal;
sp.closeMode = DetachAtClose; // sp.closeMode = DetachAtClose;
if (DebuggerRunControl *rc = createDebugger(sp)) // if (DebuggerRunControl *rc = createDebugger(sp))
startDebugger(rc); // startDebugger(rc);
} //}
void DebuggerPluginPrivate::attachCore() void DebuggerPluginPrivate::attachCore()
{ {
@@ -1637,11 +1637,6 @@ void DebuggerPluginPrivate::startRemoteCdbSession()
startDebugger(rc); startDebugger(rc);
} }
//bool DebuggerPluginPrivate::queryRemoteParameters(DebuggerStartParameters &sp, bool useScript)
//{
// return StartRemoteDialog::run(mainWindow(), m_coreSettings, useScript, &sp);
//}
void DebuggerPluginPrivate::startRemoteProcess() void DebuggerPluginPrivate::startRemoteProcess()
{ {
DebuggerStartParameters sp; DebuggerStartParameters sp;
@@ -1665,16 +1660,68 @@ void DebuggerPluginPrivate::attachToRemoteServer()
} }
} }
//const char LastProfile[] = "Debugger/LastProfile";
//const char LastDevice[] = "Debugger/LastDevice";
//const char LastProcessName[] = "Debugger/LastProcessName";
//const char LastLocalExecutable[] = "Debugger/LastLocalExecutable";
void DebuggerPluginPrivate::startRemoteServer() void DebuggerPluginPrivate::startRemoteServer()
{ {
StartGdbServerDialog dlg(mainWindow()); attachToProcess(true);
dlg.startGdbServer();
} }
void DebuggerPluginPrivate::attachToRunningApplication() void DebuggerPluginPrivate::attachToRunningApplication()
{ {
StartGdbServerDialog dlg(mainWindow()); attachToProcess(false);
dlg.attachToRemoteProcess(); }
void DebuggerPluginPrivate::attachToProcess(bool startServerOnly)
{
DeviceProcessesDialog *dlg = new DeviceProcessesDialog(mainWindow());
dlg->showAllDevices();
if (dlg->exec() == QDialog::Rejected) {
delete dlg;
return;
}
dlg->setAttribute(Qt::WA_DeleteOnClose);
ProfileChooser *profileChooser = dlg->profileChooser();
Profile *profile = profileChooser->currentProfile();
QTC_ASSERT(profile, return);
IDevice::ConstPtr device = DeviceProfileInformation::device(profile);
QTC_ASSERT(device, return);
DeviceProcess process = dlg->currentProcess();
if (process.pid == 0) {
QMessageBox::warning(mainWindow(), tr("Warning"),
tr("Cannot attach to process with PID 0"));
return;
}
#ifdef Q_OS_WIN
if (isWinProcessBeingDebugged(process.pid)) {
QMessageBox::warning(ICore::mainWindow(), tr("Process Already Under Debugger Control"),
tr("The process %1 is already under the control of a debugger.\n"
"Qt Creator cannot attach to it.").arg(process.pid));
return;
}
#endif
//setConfigValue(_("LastAttachExternalProfileIndex"), QVariant(dlg->profileChooser()->currentProfileId()));
if (device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
DebuggerStartParameters sp;
fillParameters(&sp, profile->id());
sp.attachPID = process.pid;
sp.displayName = tr("Process %1").arg(process.pid);
sp.executable = process.exe;
sp.startMode = AttachExternal;
sp.closeMode = DetachAtClose;
if (DebuggerRunControl *rc = createDebugger(sp))
startDebugger(rc);
} else {
GdbServerStarter *starter = new GdbServerStarter(dlg, startServerOnly);
starter->run();
}
} }
void DebuggerPluginPrivate::attachToQmlPort() void DebuggerPluginPrivate::attachToQmlPort()
@@ -2137,7 +2184,6 @@ void DebuggerPluginPrivate::setInitialState()
m_toolTipManager->closeAllToolTips(); m_toolTipManager->closeAllToolTips();
m_startLocalProcessAction->setEnabled(true); m_startLocalProcessAction->setEnabled(true);
m_attachToLocalProcessAction->setEnabled(true);
m_attachToQmlPortAction->setEnabled(true); m_attachToQmlPortAction->setEnabled(true);
m_attachToCoreAction->setEnabled(true); m_attachToCoreAction->setEnabled(true);
m_startRemoteProcessAction->setEnabled(true); m_startRemoteProcessAction->setEnabled(true);
@@ -2264,7 +2310,6 @@ void DebuggerPluginPrivate::updateState(DebuggerEngine *engine)
m_startLocalProcessAction->setEnabled(true); m_startLocalProcessAction->setEnabled(true);
m_attachToQmlPortAction->setEnabled(true); m_attachToQmlPortAction->setEnabled(true);
m_attachToLocalProcessAction->setEnabled(true);
m_attachToCoreAction->setEnabled(true); m_attachToCoreAction->setEnabled(true);
m_startRemoteProcessAction->setEnabled(true); m_startRemoteProcessAction->setEnabled(true);
m_attachToRemoteServerAction->setEnabled(true); m_attachToRemoteServerAction->setEnabled(true);
@@ -2962,10 +3007,6 @@ void DebuggerPluginPrivate::extensionsInitialized()
connect(act, SIGNAL(triggered()), SLOT(startRemoteEngine())); connect(act, SIGNAL(triggered()), SLOT(startRemoteEngine()));
#endif #endif
act = m_attachToLocalProcessAction = new QAction(this);
act->setText(tr("Attach to Running Local Application..."));
connect(act, SIGNAL(triggered()), SLOT(attachExternalApplication()));
act = m_attachToCoreAction = new QAction(this); act = m_attachToCoreAction = new QAction(this);
act->setText(tr("Load Core File...")); act->setText(tr("Load Core File..."));
connect(act, SIGNAL(triggered()), SLOT(attachCore())); connect(act, SIGNAL(triggered()), SLOT(attachCore()));
@@ -3065,11 +3106,6 @@ void DebuggerPluginPrivate::extensionsInitialized()
cmd->setDescription(tr("Start Gdbserver")); cmd->setDescription(tr("Start Gdbserver"));
mstart->addAction(cmd, Constants::G_MANUAL_REMOTE); mstart->addAction(cmd, Constants::G_MANUAL_REMOTE);
cmd = ActionManager::registerAction(m_attachToLocalProcessAction,
"Debugger.AttachToLocalProcess", globalcontext);
cmd->setAttribute(Command::CA_Hide);
mstart->addAction(cmd, Constants::G_MANUAL_REMOTE);
#ifdef WITH_LLDB #ifdef WITH_LLDB
cmd = ActionManager::registerAction(m_startRemoteLldbAction, cmd = ActionManager::registerAction(m_startRemoteLldbAction,
"Debugger.RemoteLldb", globalcontext); "Debugger.RemoteLldb", globalcontext);

View File

@@ -49,36 +49,13 @@
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QVariant> #include <QVariant>
#include <QSettings>
#include <QAction>
#include <QApplication>
#include <QComboBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton>
#include <QSortFilterProxyModel>
#include <QSpacerItem>
#include <QTextBrowser>
#include <QTreeView>
#include <QVBoxLayout>
using namespace Core; using namespace Core;
using namespace ProjectExplorer; using namespace ProjectExplorer;
using namespace QSsh; using namespace QSsh;
using namespace Utils; using namespace Utils;
const char LastProfile[] = "Debugger/LastProfile";
const char LastDevice[] = "Debugger/LastDevice";
const char LastProcessName[] = "Debugger/LastProcessName";
//const char LastLocalExecutable[] = "Debugger/LastLocalExecutable";
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
@@ -86,257 +63,92 @@ namespace Internal {
class StartGdbServerDialogPrivate class StartGdbServerDialogPrivate
{ {
public: public:
StartGdbServerDialogPrivate(StartGdbServerDialog *q); StartGdbServerDialogPrivate() {}
IDevice::ConstPtr currentDevice() const
{
Profile *profile = profileChooser->currentProfile();
return DeviceProfileInformation::device(profile);
}
DeviceProcessesDialog *dialog;
bool startServerOnly; bool startServerOnly;
DeviceProcessList *processList; DeviceProcess process;
QSortFilterProxyModel proxyModel; Profile *profile;
IDevice::ConstPtr device;
QLineEdit *processFilterLineEdit;
QTreeView *processView;
QPushButton *attachProcessButton;
QTextBrowser *textBrowser;
QPushButton *closeButton;
ProfileChooser *profileChooser;
DeviceUsedPortsGatherer gatherer; DeviceUsedPortsGatherer gatherer;
SshRemoteProcessRunner runner; SshRemoteProcessRunner runner;
QString remoteCommandLine;
QString remoteExecutable;
}; };
StartGdbServerDialogPrivate::StartGdbServerDialogPrivate(StartGdbServerDialog *q) GdbServerStarter::GdbServerStarter(DeviceProcessesDialog *dlg, bool startServerOnly)
: startServerOnly(true), processList(0) : QObject(dlg)
{ {
QSettings *settings = ICore::settings(); d = new StartGdbServerDialogPrivate;
d->dialog = dlg;
profileChooser = new ProfileChooser(q, ProfileChooser::RemoteDebugging); d->profile = dlg->profileChooser()->currentProfile();
d->process = dlg->currentProcess();
//executablePathChooser = new PathChooser(q); d->device = DeviceProfileInformation::device(d->profile);
//executablePathChooser->setExpectedKind(PathChooser::File); d->startServerOnly = startServerOnly;
//executablePathChooser->setPromptDialogTitle(StartGdbServerDialog::tr("Select Executable"));
//executablePathChooser->setPath(settings->value(LastLocalExecutable).toString());
processFilterLineEdit = new QLineEdit(q);
processFilterLineEdit->setText(settings->value(LastProcessName).toString());
processFilterLineEdit->selectAll();
processView = new QTreeView(q);
processView->setSortingEnabled(true);
processView->header()->setDefaultSectionSize(100);
processView->header()->setStretchLastSection(true);
processView->setAlternatingRowColors(true);
processView->setSelectionMode(QAbstractItemView::SingleSelection);
processView->setRootIsDecorated(false);
attachProcessButton = new QPushButton(q);
attachProcessButton->setText(StartGdbServerDialog::tr("&Attach to Selected Process"));
closeButton = new QPushButton(q);
closeButton->setText(StartGdbServerDialog::tr("Close"));
textBrowser = new QTextBrowser(q);
textBrowser->setEnabled(false);
QFormLayout *formLayout = new QFormLayout();
formLayout->addRow(StartGdbServerDialog::tr("Target:"), profileChooser);
formLayout->addRow(StartGdbServerDialog::tr("&Filter entries:"), processFilterLineEdit);
QHBoxLayout *horizontalLayout2 = new QHBoxLayout();
horizontalLayout2->addStretch(1);
horizontalLayout2->addWidget(attachProcessButton);
horizontalLayout2->addWidget(closeButton);
formLayout->addRow(processView);
formLayout->addRow(textBrowser);
formLayout->addRow(horizontalLayout2);
q->setLayout(formLayout);
} }
} // namespace Internal GdbServerStarter::~GdbServerStarter()
StartGdbServerDialog::StartGdbServerDialog(QWidget *parent) :
QDialog(parent),
d(new Internal::StartGdbServerDialogPrivate(this))
{ {
setWindowTitle(tr("List of Remote Processes"));
QObject::connect(d->closeButton, SIGNAL(clicked()), this, SLOT(reject()));
connect(&d->gatherer, SIGNAL(error(QString)), SLOT(portGathererError(QString)));
connect(&d->gatherer, SIGNAL(portListReady()), SLOT(portListReady()));
d->processView->setSelectionBehavior(QAbstractItemView::SelectRows);
d->proxyModel.setDynamicSortFilter(true);
d->proxyModel.setFilterKeyColumn(-1);
d->processView->setModel(&d->proxyModel);
connect(d->processFilterLineEdit, SIGNAL(textChanged(QString)),
&d->proxyModel, SLOT(setFilterRegExp(QString)));
connect(d->processView->selectionModel(),
SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
SLOT(updateButtons()));
connect(d->profileChooser, SIGNAL(activated(int)),
SLOT(updateButtons()));
//connect(d->updateListButton, SIGNAL(clicked()),
// SLOT(updateProcessList()));
connect(d->attachProcessButton, SIGNAL(clicked()), SLOT(attachToProcess()));
connect(&d->proxyModel, SIGNAL(layoutChanged()),
SLOT(handleProcessListUpdated()));
connect(d->profileChooser, SIGNAL(currentIndexChanged(int)),
SLOT(attachToDevice()));
updateButtons();
attachToDevice();
}
StartGdbServerDialog::~StartGdbServerDialog()
{
delete d->processList;
delete d; delete d;
} }
void StartGdbServerDialog::attachToDevice() void GdbServerStarter::handleRemoteError(const QString &errorMsg)
{ {
IDevice::ConstPtr device = d->currentDevice(); QMessageBox::critical(0, tr("Remote Error"), errorMsg);
// TODO: display error on non-matching device.
if (!device || !device->canCreateProcessModel())
return;
delete d->processList;
d->processList = device->createProcessListModel();
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) void GdbServerStarter::portGathererError(const QString &text)
{
QMessageBox::critical(this, tr("Remote Error"), errorMsg);
updateButtons();
}
void StartGdbServerDialog::handleProcessListUpdated()
{
updateButtons();
}
void StartGdbServerDialog::updateProcessList()
{
d->attachProcessButton->setEnabled(false);
d->processList->update();
d->proxyModel.setFilterRegExp(QString());
d->proxyModel.setFilterRegExp(d->processFilterLineEdit->text());
updateButtons();
}
void StartGdbServerDialog::attachToProcess()
{
const QModelIndexList indexes =
d->processView->selectionModel()->selectedIndexes();
if (indexes.empty())
return;
d->attachProcessButton->setEnabled(false);
IDevice::ConstPtr device = d->currentDevice();
if (!device)
return;
PortList ports = device->freePorts();
const int port = d->gatherer.getNextFreePort(&ports);
const int row = d->proxyModel.mapToSource(indexes.first()).row();
QTC_ASSERT(row >= 0, return);
DeviceProcess process = d->processList->at(row);
d->remoteCommandLine = process.cmdLine;
d->remoteExecutable = process.exe;
if (port == -1) {
reportFailure();
return;
}
QSettings *settings = ICore::settings();
settings->setValue(LastProfile, d->profileChooser->currentProfileId().toString());
settings->setValue(LastProcessName, d->processFilterLineEdit->text());
startGdbServerOnPort(port, process.pid);
}
void StartGdbServerDialog::reportFailure()
{
QTC_ASSERT(false, /**/);
logMessage(tr("Process aborted"));
}
void StartGdbServerDialog::logMessage(const QString &line)
{
d->textBrowser->append(line);
}
void StartGdbServerDialog::handleProcessKilled()
{
updateProcessList();
}
void StartGdbServerDialog::updateButtons()
{
d->attachProcessButton->setEnabled(d->processView->selectionModel()->hasSelection()
|| d->proxyModel.rowCount() == 1);
}
void StartGdbServerDialog::portGathererError(const QString &text)
{ {
logMessage(tr("Could not retrieve list of free ports:")); logMessage(tr("Could not retrieve list of free ports:"));
logMessage(text); logMessage(text);
reportFailure(); logMessage(tr("Process aborted"));
} }
void StartGdbServerDialog::portListReady() void GdbServerStarter::run()
{ {
updateButtons(); QTC_ASSERT(d->device, return);
connect(&d->gatherer, SIGNAL(error(QString)), SLOT(portGathererError(QString)));
connect(&d->gatherer, SIGNAL(portListReady()), SLOT(portListReady()));
d->gatherer.start(d->device);
} }
void StartGdbServerDialog::startGdbServer() void GdbServerStarter::portListReady()
{ {
d->startServerOnly = true; PortList ports = d->device->freePorts();
if (exec() == QDialog::Rejected) const int port = d->gatherer.getNextFreePort(&ports);
if (port == -1) {
QTC_ASSERT(false, /**/);
emit logMessage(tr("Process aborted"));
return; return;
d->gatherer.start(d->currentDevice()); }
connect(&d->runner, SIGNAL(connectionError()), SLOT(handleConnectionError()));
connect(&d->runner, SIGNAL(processStarted()), SLOT(handleProcessStarted()));
connect(&d->runner, SIGNAL(readyReadStandardOutput()), SLOT(handleProcessOutputAvailable()));
connect(&d->runner, SIGNAL(readyReadStandardError()), SLOT(handleProcessErrorOutput()));
connect(&d->runner, SIGNAL(processClosed(int)), SLOT(handleProcessClosed(int)));
QByteArray cmd = "/usr/bin/gdbserver --attach :"
+ QByteArray::number(port) + " " + QByteArray::number(d->process.pid);
logMessage(tr("Running command: %1").arg(QString::fromLatin1(cmd)));
d->runner.run(cmd, d->device->sshParameters());
} }
void StartGdbServerDialog::attachToRemoteProcess() void GdbServerStarter::handleConnectionError()
{
d->startServerOnly = false;
if (exec() == QDialog::Rejected)
return;
d->gatherer.start(d->currentDevice());
}
void StartGdbServerDialog::handleConnectionError()
{ {
logMessage(tr("Connection error: %1").arg(d->runner.lastConnectionErrorString())); logMessage(tr("Connection error: %1").arg(d->runner.lastConnectionErrorString()));
emit processAborted();
} }
void StartGdbServerDialog::handleProcessStarted() void GdbServerStarter::handleProcessStarted()
{ {
logMessage(tr("Starting gdbserver...")); logMessage(tr("Starting gdbserver..."));
} }
void StartGdbServerDialog::handleProcessOutputAvailable() void GdbServerStarter::handleProcessOutputAvailable()
{ {
logMessage(QString::fromUtf8(d->runner.readAllStandardOutput().trimmed())); logMessage(QString::fromUtf8(d->runner.readAllStandardOutput().trimmed()));
} }
void StartGdbServerDialog::handleProcessErrorOutput() void GdbServerStarter::handleProcessErrorOutput()
{ {
const QByteArray ba = d->runner.readAllStandardError(); const QByteArray ba = d->runner.readAllStandardError();
logMessage(QString::fromUtf8(ba.trimmed())); logMessage(QString::fromUtf8(ba.trimmed()));
@@ -345,33 +157,25 @@ void StartGdbServerDialog::handleProcessErrorOutput()
foreach (const QByteArray &line, ba.split('\n')) { foreach (const QByteArray &line, ba.split('\n')) {
if (line.startsWith("Listening on port")) { if (line.startsWith("Listening on port")) {
const int port = line.mid(18).trimmed().toInt(); const int port = line.mid(18).trimmed().toInt();
reportOpenPort(port); logMessage(tr("Port %1 is now accessible.").arg(port));
logMessage(tr("Server started on %1:%2")
.arg(d->device->sshParameters().host).arg(port));
if (!d->startServerOnly)
attach(port);
} }
} }
} }
void StartGdbServerDialog::reportOpenPort(int port) void GdbServerStarter::attach(int port)
{ {
close(); QString sysroot = SysRootProfileInformation::sysRoot(d->profile).toString();
logMessage(tr("Port %1 is now accessible.").arg(port));
IDevice::ConstPtr device = d->currentDevice();
QString channel = QString("%1:%2").arg(device->sshParameters().host).arg(port);
logMessage(tr("Server started on %1").arg(channel));
const Profile *profile = d->profileChooser->currentProfile();
QTC_ASSERT(profile, return);
if (d->startServerOnly) {
//showStatusMessage(tr("gdbserver is now listening at %1").arg(channel));
} else {
QString sysroot = SysRootProfileInformation::sysRoot(profile).toString();
QString binary; QString binary;
QString localExecutable; QString localExecutable;
QString candidate = sysroot + d->remoteExecutable; QString candidate = sysroot + d->process.exe;
if (QFileInfo(candidate).exists()) if (QFileInfo(candidate).exists())
localExecutable = candidate; localExecutable = candidate;
if (localExecutable.isEmpty()) { if (localExecutable.isEmpty()) {
binary = d->remoteCommandLine.section(QLatin1Char(' '), 0, 0); binary = d->process.cmdLine.section(QLatin1Char(' '), 0, 0);
candidate = sysroot + QLatin1Char('/') + binary; candidate = sysroot + QLatin1Char('/') + binary;
if (QFileInfo(candidate).exists()) if (QFileInfo(candidate).exists())
localExecutable = candidate; localExecutable = candidate;
@@ -389,7 +193,7 @@ void StartGdbServerDialog::reportOpenPort(int port)
if (localExecutable.isEmpty()) { if (localExecutable.isEmpty()) {
QMessageBox::warning(DebuggerPlugin::mainWindow(), tr("Warning"), QMessageBox::warning(DebuggerPlugin::mainWindow(), tr("Warning"),
tr("Cannot find local executable for remote process \"%1\".") tr("Cannot find local executable for remote process \"%1\".")
.arg(d->remoteCommandLine)); .arg(d->process.exe));
return; return;
} }
@@ -397,10 +201,12 @@ void StartGdbServerDialog::reportOpenPort(int port)
if (abis.isEmpty()) { if (abis.isEmpty()) {
QMessageBox::warning(DebuggerPlugin::mainWindow(), tr("Warning"), QMessageBox::warning(DebuggerPlugin::mainWindow(), tr("Warning"),
tr("Cannot find ABI for remote process \"%1\".") tr("Cannot find ABI for remote process \"%1\".")
.arg(d->remoteCommandLine)); .arg(d->process.exe));
return; return;
} }
QString channel = QString("%1:%2").arg(d->device->sshParameters().host).arg(port);
DebuggerStartParameters sp; DebuggerStartParameters sp;
sp.displayName = tr("Remote: \"%1\"").arg(channel); sp.displayName = tr("Remote: \"%1\"").arg(channel);
sp.remoteChannel = channel; sp.remoteChannel = channel;
@@ -410,35 +216,25 @@ void StartGdbServerDialog::reportOpenPort(int port)
sp.overrideStartScript.clear(); sp.overrideStartScript.clear();
sp.useServerStartScript = false; sp.useServerStartScript = false;
sp.serverStartScript.clear(); sp.serverStartScript.clear();
sp.sysRoot = SysRootProfileInformation::sysRoot(profile).toString(); sp.sysRoot = SysRootProfileInformation::sysRoot(d->profile).toString();
sp.debuggerCommand = DebuggerProfileInformation::debuggerCommand(profile).toString(); sp.debuggerCommand = DebuggerProfileInformation::debuggerCommand(d->profile).toString();
sp.connParams = device->sshParameters(); sp.connParams = d->device->sshParameters();
if (ToolChain *tc = ToolChainProfileInformation::toolChain(profile)) if (ToolChain *tc = ToolChainProfileInformation::toolChain(d->profile))
sp.toolChainAbi = tc->targetAbi(); sp.toolChainAbi = tc->targetAbi();
if (RunControl *rc = DebuggerPlugin::createDebugger(sp)) if (RunControl *rc = DebuggerPlugin::createDebugger(sp))
DebuggerPlugin::startDebugger(rc); DebuggerPlugin::startDebugger(rc);
}
} }
void StartGdbServerDialog::handleProcessClosed(int status) void GdbServerStarter::handleProcessClosed(int status)
{ {
logMessage(tr("Process gdbserver finished. Status: %1").arg(status)); logMessage(tr("Process gdbserver finished. Status: %1").arg(status));
} }
void StartGdbServerDialog::startGdbServerOnPort(int port, int pid) void GdbServerStarter::logMessage(const QString &line)
{ {
IDevice::ConstPtr device = d->currentDevice(); d->dialog->logMessage(line);
connect(&d->runner, SIGNAL(connectionError()), SLOT(handleConnectionError()));
connect(&d->runner, SIGNAL(processStarted()), SLOT(handleProcessStarted()));
connect(&d->runner, SIGNAL(readyReadStandardOutput()), SLOT(handleProcessOutputAvailable()));
connect(&d->runner, SIGNAL(readyReadStandardError()), SLOT(handleProcessErrorOutput()));
connect(&d->runner, SIGNAL(processClosed(int)), SLOT(handleProcessClosed(int)));
QByteArray cmd = "/usr/bin/gdbserver --attach :"
+ QByteArray::number(port) + " " + QByteArray::number(pid);
logMessage(tr("Running command: %1").arg(QString::fromLatin1(cmd)));
d->runner.run(cmd, device->sshParameters());
} }
} // namespace Internal
} // namespace Debugger } // namespace Debugger

View File

@@ -32,35 +32,26 @@
#define STARTGDBSERVERDIALOG_H #define STARTGDBSERVERDIALOG_H
#include "debugger_global.h" #include "debugger_global.h"
#include <projectexplorer/profile.h>
#include <QDialog> #include <projectexplorer/devicesupport/deviceprocessesdialog.h>
namespace Debugger { namespace Debugger {
namespace Internal {
namespace Internal { class StartGdbServerDialogPrivate; } class StartGdbServerDialogPrivate;
class DEBUGGER_EXPORT StartGdbServerDialog : public QDialog class GdbServerStarter : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
StartGdbServerDialog(QWidget *parent); GdbServerStarter(ProjectExplorer::DeviceProcessesDialog *dlg, bool startServerOnly);
~StartGdbServerDialog(); ~GdbServerStarter();
void startGdbServer(); void run();
void attachToRemoteProcess();
signals:
void processAborted();
private slots: private slots:
void attachToDevice();
void handleRemoteError(const QString &errorMessage); void handleRemoteError(const QString &errorMessage);
void handleProcessListUpdated();
void updateProcessList();
void attachToProcess();
void handleProcessKilled();
void updateButtons();
void portGathererError(const QString &errorMessage); void portGathererError(const QString &errorMessage);
void portListReady(); void portListReady();
@@ -71,13 +62,12 @@ private slots:
void handleConnectionError(); void handleConnectionError();
private: private:
void startGdbServerOnPort(int port, int pid); void attach(int port);
void reportOpenPort(int port);
void reportFailure();
void logMessage(const QString &line); void logMessage(const QString &line);
Internal::StartGdbServerDialogPrivate *d; StartGdbServerDialogPrivate *d;
}; };
} // namespace Internal
} // namespace Debugger } // namespace Debugger
#endif // STARTGDBSERVERDIALOG_H #endif // STARTGDBSERVERDIALOG_H

View File

@@ -29,106 +29,299 @@
#include "devicesupport/deviceprocessesdialog.h" #include "devicesupport/deviceprocessesdialog.h"
#include "devicesupport/deviceprocesslist.h" #include "devicesupport/deviceprocesslist.h"
#include "ui_deviceprocessesdialog.h" #include "profilechooser.h"
#include "profileinformation.h"
#include <utils/filterlineedit.h>
#include <utils/qtcassert.h>
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QSpacerItem>
#include <QTextBrowser>
#include <QTextDocument>
#include <QTreeView>
#include <QVBoxLayout>
using namespace Utils;
namespace ProjectExplorer { namespace ProjectExplorer {
namespace Internal { namespace Internal {
class DeviceProcessesDialogPrivate ///////////////////////////////////////////////////////////////////////
//
// ProcessListFilterModel
//
///////////////////////////////////////////////////////////////////////
class ProcessListFilterModel : public QSortFilterProxyModel
{ {
public: public:
DeviceProcessesDialogPrivate(DeviceProcessList *processList) ProcessListFilterModel();
: processList(processList) bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
{
}
Ui::DeviceProcessesDialog ui;
DeviceProcessList * const processList;
QSortFilterProxyModel proxyModel;
}; };
ProcessListFilterModel::ProcessListFilterModel()
DeviceProcessesDialog::DeviceProcessesDialog(DeviceProcessList *processList, QWidget *parent)
: QDialog(parent), d(new DeviceProcessesDialogPrivate(processList))
{ {
processList->setParent(this); setFilterCaseSensitivity(Qt::CaseInsensitive);
setDynamicSortFilter(true);
setFilterKeyColumn(-1);
}
d->ui.setupUi(this); bool ProcessListFilterModel::lessThan(const QModelIndex &left,
d->proxyModel.setSourceModel(d->processList); const QModelIndex &right) const
d->proxyModel.setDynamicSortFilter(true); {
d->proxyModel.setFilterKeyColumn(-1); const QString l = sourceModel()->data(left).toString();
d->ui.treeView->setModel(&d->proxyModel); const QString r = sourceModel()->data(right).toString();
d->ui.treeView->setSelectionBehavior(QAbstractItemView::SelectRows); if (left.column() == 0)
d->ui.treeView->setUniformRowHeights(true); return l.toInt() < r.toInt();
connect(d->ui.processFilterLineEdit, SIGNAL(textChanged(QString)), return l < r;
&d->proxyModel, SLOT(setFilterRegExp(QString))); }
connect(d->ui.treeView->selectionModel(), ///////////////////////////////////////////////////////////////////////
//
// DeviceProcessesDialogPrivate
//
///////////////////////////////////////////////////////////////////////
class DeviceProcessesDialogPrivate : public QObject
{
Q_OBJECT
public:
DeviceProcessesDialogPrivate(QWidget *parent);
public slots:
void setDevice(const IDevice::ConstPtr &device);
void updateProcessList();
void updateDevice();
void killProcess();
void handleRemoteError(const QString &errorMsg);
void handleProcessListUpdated();
void handleProcessKilled();
void updateButtons();
DeviceProcess selectedProcess() const;
public:
QWidget *q;
DeviceProcessList *processList;
ProcessListFilterModel proxyModel;
ProfileChooser *profileChooser;
QTreeView *procView;
QTextBrowser *errorText;
FilterLineEdit *processFilterLineEdit;
QPushButton *updateListButton;
QPushButton *killProcessButton;
QPushButton *attachProcessButton;
};
DeviceProcessesDialogPrivate::DeviceProcessesDialogPrivate(QWidget *parent)
: q(parent)
{
processList = 0;
processFilterLineEdit = new FilterLineEdit(q);
processFilterLineEdit->setPlaceholderText(DeviceProcessesDialog::tr("Filter"));
processFilterLineEdit->setFocus(Qt::TabFocusReason);
profileChooser = new ProfileChooser(q);
procView = new QTreeView(q);
procView->setModel(&proxyModel);
procView->setSelectionBehavior(QAbstractItemView::SelectRows);
procView->setSelectionMode(QAbstractItemView::SingleSelection);
procView->setUniformRowHeights(true);
procView->setRootIsDecorated(false);
procView->setAlternatingRowColors(true);
procView->setSortingEnabled(true);
procView->header()->setDefaultSectionSize(100);
procView->header()->setStretchLastSection(true);
procView->sortByColumn(1, Qt::AscendingOrder);
errorText = new QTextBrowser(q);
updateListButton = new QPushButton(DeviceProcessesDialog::tr("&Update List"), q);
killProcessButton = new QPushButton(DeviceProcessesDialog::tr("&Kill Process"), q);
attachProcessButton = new QPushButton(DeviceProcessesDialog::tr("&Attach to Process"), q);
QDialogButtonBox *buttonBox = new QDialogButtonBox(q);
buttonBox->setStandardButtons(QDialogButtonBox::Close);
buttonBox->addButton(updateListButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(killProcessButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(attachProcessButton, QDialogButtonBox::AcceptRole);
QFormLayout *leftColumn = new QFormLayout();
leftColumn->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
leftColumn->addRow(DeviceProcessesDialog::tr("Target:"), profileChooser);
leftColumn->addRow(DeviceProcessesDialog::tr("&Filter:"), processFilterLineEdit);
// QVBoxLayout *rightColumn = new QVBoxLayout();
// rightColumn->addWidget(updateListButton);
// rightColumn->addWidget(killProcessButton);
// rightColumn->addStretch();
// QHBoxLayout *horizontalLayout = new QHBoxLayout();
// horizontalLayout->addLayout(leftColumn);
// horizontalLayout->addLayout(rightColumn);
QVBoxLayout *mainLayout = new QVBoxLayout(q);
mainLayout->addLayout(leftColumn);
mainLayout->addWidget(procView);
mainLayout->addWidget(errorText);
mainLayout->addWidget(buttonBox);
// QFrame *line = new QFrame(this);
// line->setFrameShape(QFrame::HLine);
// line->setFrameShadow(QFrame::Sunken);
connect(processFilterLineEdit, SIGNAL(textChanged(QString)),
&proxyModel, SLOT(setFilterRegExp(QString)));
connect(procView->selectionModel(),
SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
SLOT(handleSelectionChanged())); SLOT(updateButtons()));
connect(d->ui.updateListButton, SIGNAL(clicked()), connect(updateListButton, SIGNAL(clicked()), SLOT(updateProcessList()));
SLOT(updateProcessList())); connect(profileChooser, SIGNAL(currentIndexChanged(int)), SLOT(updateDevice()));
connect(d->ui.killProcessButton, SIGNAL(clicked()), SLOT(killProcess())); connect(killProcessButton, SIGNAL(clicked()), SLOT(killProcess()));
connect(d->processList, SIGNAL(error(QString)), connect(&proxyModel, SIGNAL(layoutChanged()), SLOT(handleProcessListUpdated()));
connect(buttonBox, SIGNAL(accepted()), q, SLOT(accept()));
connect(buttonBox, SIGNAL(rejected()), q, SLOT(reject()));
}
void DeviceProcessesDialogPrivate::setDevice(const IDevice::ConstPtr &device)
{
delete processList;
processList = device->createProcessListModel();
QTC_ASSERT(processList, return);
proxyModel.setSourceModel(processList);
connect(processList, SIGNAL(error(QString)),
SLOT(handleRemoteError(QString))); SLOT(handleRemoteError(QString)));
connect(d->processList, SIGNAL(processListUpdated()), connect(processList, SIGNAL(processListUpdated()),
SLOT(handleProcessListUpdated())); SLOT(handleProcessListUpdated()));
connect(d->processList, SIGNAL(processKilled()), connect(processList, SIGNAL(processKilled()),
SLOT(handleProcessKilled()), Qt::QueuedConnection); SLOT(handleProcessKilled()), Qt::QueuedConnection);
connect(&d->proxyModel, SIGNAL(layoutChanged()),
SLOT(handleProcessListUpdated())); updateButtons();
handleSelectionChanged();
updateProcessList(); updateProcessList();
} }
void DeviceProcessesDialogPrivate::handleRemoteError(const QString &errorMsg)
{
QMessageBox::critical(q, tr("Remote Error"), errorMsg);
updateListButton->setEnabled(true);
updateButtons();
}
void DeviceProcessesDialogPrivate::handleProcessListUpdated()
{
updateListButton->setEnabled(true);
procView->resizeColumnToContents(0);
procView->resizeColumnToContents(1);
updateButtons();
}
void DeviceProcessesDialogPrivate::updateProcessList()
{
updateListButton->setEnabled(false);
killProcessButton->setEnabled(false);
if (processList)
processList->update();
}
void DeviceProcessesDialogPrivate::killProcess()
{
const QModelIndexList indexes = procView->selectionModel()->selectedIndexes();
if (indexes.empty())
return;
updateListButton->setEnabled(false);
killProcessButton->setEnabled(false);
processList->killProcess(proxyModel.mapToSource(indexes.first()).row());
}
void DeviceProcessesDialogPrivate::updateDevice()
{
setDevice(DeviceProfileInformation::device(profileChooser->currentProfile()));
}
void DeviceProcessesDialogPrivate::handleProcessKilled()
{
updateProcessList();
}
void DeviceProcessesDialogPrivate::updateButtons()
{
bool hasSelection = procView->selectionModel()->hasSelection();
attachProcessButton->setEnabled(hasSelection);
killProcessButton->setEnabled(hasSelection);
errorText->setVisible(!errorText->document()->isEmpty());
}
DeviceProcess DeviceProcessesDialogPrivate::selectedProcess() const
{
const QModelIndexList indexes = procView->selectionModel()->selectedIndexes();
if (indexes.empty())
return DeviceProcess();
return processList->at(proxyModel.mapToSource(indexes.first()).row());
}
} // namespace Internal
///////////////////////////////////////////////////////////////////////
//
// DeviceProcessesDialog
//
///////////////////////////////////////////////////////////////////////
DeviceProcessesDialog::DeviceProcessesDialog(QWidget *parent)
: QDialog(parent), d(new Internal::DeviceProcessesDialogPrivate(this))
{
setWindowTitle(tr("List of Processes"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setMinimumHeight(500);
}
DeviceProcessesDialog::~DeviceProcessesDialog() DeviceProcessesDialog::~DeviceProcessesDialog()
{ {
delete d; delete d;
} }
void DeviceProcessesDialog::handleRemoteError(const QString &errorMsg) void DeviceProcessesDialog::setDevice(const IDevice::ConstPtr &device)
{ {
QMessageBox::critical(this, tr("Remote Error"), errorMsg); d->profileChooser->hide();
d->ui.updateListButton->setEnabled(true); d->setDevice(device);
handleSelectionChanged();
} }
void DeviceProcessesDialog::handleProcessListUpdated() void DeviceProcessesDialog::showAllDevices()
{ {
d->ui.updateListButton->setEnabled(true); d->profileChooser->show();
handleSelectionChanged(); d->updateDevice();
} }
void DeviceProcessesDialog::updateProcessList() DeviceProcess DeviceProcessesDialog::currentProcess() const
{ {
d->ui.updateListButton->setEnabled(false); return d->selectedProcess();
d->ui.killProcessButton->setEnabled(false);
d->processList->update();
} }
void DeviceProcessesDialog::killProcess() ProfileChooser *DeviceProcessesDialog::profileChooser() const
{ {
const QModelIndexList &indexes return d->profileChooser;
= d->ui.treeView->selectionModel()->selectedIndexes();
if (indexes.empty())
return;
d->ui.updateListButton->setEnabled(false);
d->ui.killProcessButton->setEnabled(false);
d->processList->killProcess(d->proxyModel.mapToSource(indexes.first()).row());
} }
void DeviceProcessesDialog::handleProcessKilled() void DeviceProcessesDialog::logMessage(const QString &line)
{ {
updateProcessList(); d->errorText->setVisible(true);
d->errorText->append(line);
} }
void DeviceProcessesDialog::handleSelectionChanged() } // namespace ProjectExplorer
{
d->ui.killProcessButton->setEnabled(d->ui.treeView->selectionModel()->hasSelection());
}
} // namespace Internal #include "deviceprocessesdialog.moc"
} // namespace RemoteLinux

View File

@@ -32,37 +32,36 @@
#include "../projectexplorer_export.h" #include "../projectexplorer_export.h"
#include <projectexplorer/profile.h>
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/devicesupport/deviceprocesslist.h>
#include <QDialog> #include <QDialog>
namespace ProjectExplorer { namespace ProjectExplorer {
class DeviceProcessList; class ProfileChooser;
namespace Internal { namespace Internal { class DeviceProcessesDialogPrivate; }
class DeviceProcessesDialogPrivate;
class DeviceProcessesDialog : public QDialog class PROJECTEXPLORER_EXPORT DeviceProcessesDialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
// Note: The dialog takes ownership of processList. explicit DeviceProcessesDialog(QWidget *parent = 0);
explicit DeviceProcessesDialog(DeviceProcessList *processList, QWidget *parent = 0);
~DeviceProcessesDialog(); ~DeviceProcessesDialog();
private slots: void setDevice(const IDevice::ConstPtr &device);
void updateProcessList(); void showAllDevices();
void killProcess(); DeviceProcess currentProcess() const;
void handleRemoteError(const QString &errorMsg); ProfileChooser *profileChooser() const;
void handleProcessListUpdated(); void logMessage(const QString &line);
void handleProcessKilled();
void handleSelectionChanged();
private: private:
Internal::DeviceProcessesDialogPrivate * const d; Internal::DeviceProcessesDialogPrivate * const d;
}; };
} // namespace Internal
} // namespace RemoteLinux } // namespace RemoteLinux
#endif // REMOTELINUXPROCESSESDIALOG_H #endif // REMOTELINUXPROCESSESDIALOG_H

View File

@@ -1,132 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ProjectExplorer::Internal::DeviceProcessesDialog</class>
<widget class="QDialog" name="ProjectExplorer::Internal::DeviceProcessesDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>766</width>
<height>684</height>
</rect>
</property>
<property name="windowTitle">
<string>List of Remote Processes</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="filterLabel">
<property name="text">
<string>&amp;Filter entries:</string>
</property>
<property name="buddy">
<cstring>processFilterLineEdit</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="processFilterLineEdit"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTreeView" name="treeView"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="updateListButton">
<property name="text">
<string>&amp;Update List</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="killProcessButton">
<property name="text">
<string>&amp;Kill Selected Process</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ProjectExplorer::Internal::DeviceProcessesDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>257</x>
<y>290</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ProjectExplorer::Internal::DeviceProcessesDialog</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

@@ -134,7 +134,10 @@ int DeviceProcessList::rowCount(const QModelIndex &parent) const
return parent.isValid() ? 0 : d->remoteProcesses.count(); return parent.isValid() ? 0 : d->remoteProcesses.count();
} }
int DeviceProcessList::columnCount(const QModelIndex &) const { return 2; } int DeviceProcessList::columnCount(const QModelIndex &) const
{
return 2;
}
QVariant DeviceProcessList::headerData(int section, Qt::Orientation orientation, QVariant DeviceProcessList::headerData(int section, Qt::Orientation orientation,
int role) const int role) const
@@ -142,7 +145,7 @@ QVariant DeviceProcessList::headerData(int section, Qt::Orientation orientation,
if (orientation != Qt::Horizontal || role != Qt::DisplayRole || section < 0 if (orientation != Qt::Horizontal || role != Qt::DisplayRole || section < 0
|| section >= columnCount()) || section >= columnCount())
return QVariant(); return QVariant();
return section == 0? tr("PID") : tr("Command Line"); return section == 0? tr("Process ID") : tr("Command Line");
} }
QVariant DeviceProcessList::data(const QModelIndex &index, int role) const QVariant DeviceProcessList::data(const QModelIndex &index, int role) const

View File

@@ -248,7 +248,7 @@ int DeviceSettingsWidget::currentIndex() const
return m_ui->configurationComboBox->currentIndex(); return m_ui->configurationComboBox->currentIndex();
} }
QSharedPointer<const IDevice> DeviceSettingsWidget::currentDevice() const IDevice::ConstPtr DeviceSettingsWidget::currentDevice() const
{ {
Q_ASSERT(currentIndex() != -1); Q_ASSERT(currentIndex() != -1);
return m_deviceManagerModel->device(currentIndex()); return m_deviceManagerModel->device(currentIndex());
@@ -335,8 +335,9 @@ void DeviceSettingsWidget::handleAdditionalActionRequest(int actionId)
void DeviceSettingsWidget::handleProcessListRequested() void DeviceSettingsWidget::handleProcessListRequested()
{ {
QTC_ASSERT(currentDevice()->canCreateProcessModel(), return); QTC_ASSERT(currentDevice()->canCreateProcessModel(), return);
DeviceProcessesDialog d(currentDevice()->createProcessListModel()); DeviceProcessesDialog dlg;
d.exec(); dlg.setDevice(currentDevice());
dlg.exec();
} }
} // namespace Internal } // namespace Internal

View File

@@ -30,6 +30,7 @@
#ifndef DEVICESETTINGSWIDGET_H #ifndef DEVICESETTINGSWIDGET_H
#define DEVICESETTINGSWIDGET_H #define DEVICESETTINGSWIDGET_H
#include "devicesupport/idevice.h"
#include <coreplugin/id.h> #include <coreplugin/id.h>
#include <QList> #include <QList>
@@ -75,7 +76,7 @@ private:
void initGui(); void initGui();
void displayCurrent(); void displayCurrent();
void setDeviceInfoWidgetsEnabled(bool enable); void setDeviceInfoWidgetsEnabled(bool enable);
QSharedPointer<const IDevice> currentDevice() const; IDevice::ConstPtr currentDevice() const;
int currentIndex() const; int currentIndex() const;
void clearDetails(); void clearDetails();
QString parseTestOutput(); QString parseTestOutput();

View File

@@ -174,7 +174,7 @@ void LocalProcessList::handleWindowsUpdate()
CloseHandle(snapshot); CloseHandle(snapshot);
reportProcessListUpdated(processes); reportProcessListUpdated(processes);
#endif //Q_OS_WIN #endif
} }
void LocalProcessList::handlePsError() void LocalProcessList::handlePsError()

View File

@@ -245,7 +245,6 @@ FORMS += processstep.ui \
publishing/publishingwizardselectiondialog.ui \ publishing/publishingwizardselectiondialog.ui \
codestylesettingspropertiespage.ui \ codestylesettingspropertiespage.ui \
devicesupport/devicefactoryselectiondialog.ui \ devicesupport/devicefactoryselectiondialog.ui \
devicesupport/deviceprocessesdialog.ui \
devicesupport/devicesettingswidget.ui devicesupport/devicesettingswidget.ui
WINSOURCES += \ WINSOURCES += \

View File

@@ -302,7 +302,6 @@ QtcPlugin {
"devicesupport/sshdeviceprocesslist.h", "devicesupport/sshdeviceprocesslist.h",
"devicesupport/deviceprocessesdialog.cpp", "devicesupport/deviceprocessesdialog.cpp",
"devicesupport/deviceprocessesdialog.h", "devicesupport/deviceprocessesdialog.h",
"devicesupport/deviceprocessesdialog.ui",
"devicesupport/devicesettingswidget.cpp", "devicesupport/devicesettingswidget.cpp",
"devicesupport/devicesettingswidget.h", "devicesupport/devicesettingswidget.h",
"devicesupport/devicesettingswidget.ui", "devicesupport/devicesettingswidget.ui",