Commit 7a6beafa authored by hjk's avatar hjk
Browse files

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: default avatarThomas Hartmann <Thomas.Hartmann@nokia.com>
Reviewed-by: default avatarhjk <qthjk@ovi.com>
parent 389dc900
......@@ -41,7 +41,6 @@
#include <projectexplorer/abi.h>
#include <projectexplorer/profilechooser.h>
#include <projectexplorer/profileinformation.h>
#include <utils/filterlineedit.h>
#include <utils/historycompleter.h>
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
......@@ -51,7 +50,6 @@
#include <QApplication>
#include <QButtonGroup>
#include <QCheckBox>
#include <QCoreApplication>
#include <QDebug>
#include <QDialog>
#include <QDialogButtonBox>
......@@ -68,7 +66,6 @@
#include <QRadioButton>
#include <QRegExp>
#include <QScrollArea>
#include <QSortFilterProxyModel>
#include <QSpinBox>
#include <QStandardItemModel>
#include <QTreeView>
......@@ -79,301 +76,15 @@ using namespace Core;
using namespace ProjectExplorer;
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
//
///////////////////////////////////////////////////////////////////////
namespace Debugger {
namespace Internal {
class StartExternalParameters
{
public:
......
......@@ -52,7 +52,6 @@ class DebuggerStartParameters;
namespace Internal {
class AttachCoreDialogPrivate;
class AttachExternalDialogPrivate;
class AttachToQmlPortDialogPrivate;
class ProcessListFilterModel;
class StartExternalDialogPrivate;
......@@ -61,37 +60,6 @@ class StartRemoteDialogPrivate;
class StartRemoteEngineDialogPrivate;
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
{
Q_OBJECT
......
......@@ -63,6 +63,7 @@
#include "debuggertooltipmanager.h"
#include "localsandexpressionswindow.h"
#include "loadcoredialog.h"
#include "hostutils.h"
#include "snapshothandler.h"
#include "threadshandler.h"
......@@ -94,11 +95,13 @@
#include <projectexplorer/abi.h>
#include <projectexplorer/applicationrunconfiguration.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/devicesupport/deviceprocessesdialog.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorersettings.h>
#include <projectexplorer/project.h>
#include <projectexplorer/session.h>
#include <projectexplorer/profilechooser.h>
#include <projectexplorer/profileinformation.h>
#include <projectexplorer/profilemanager.h>
#include <projectexplorer/target.h>
......@@ -776,13 +779,12 @@ public slots:
void startRemoteCdbSession();
void startRemoteProcess();
void startRemoteServer();
//bool queryRemoteParameters(DebuggerStartParameters &sp, bool useScript);
void attachToRemoteServer();
void attachToProcess(bool startServerOnly);
void attachToRunningApplication();
void attachToQmlPort();
void startRemoteEngine();
void attachExternalApplication();
Q_SLOT void attachExternalApplication(ProjectExplorer::RunControl *rc);
//Q_SLOT void attachToLocalProcess(ProjectExplorer::RunControl *rc);
void runScheduled();
void attachCore();
......@@ -1122,7 +1124,6 @@ public:
QAction *m_attachToRemoteServerAction;
QAction *m_startRemoteCdbAction;
QAction *m_startRemoteLldbAction;
QAction *m_attachToLocalProcessAction;
QAction *m_attachToCoreAction;
QAction *m_detachAction;
QAction *m_continueAction;
......@@ -1245,7 +1246,6 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(DebuggerPlugin *plugin) :
m_attachToQmlPortAction = 0;
m_startRemoteCdbAction = 0;
m_startRemoteLldbAction = 0;
m_attachToLocalProcessAction = 0;
m_attachToCoreAction = 0;
m_detachAction = 0;
......@@ -1520,44 +1520,44 @@ void DebuggerPluginPrivate::startExternalApplication()
startDebugger(rc);
}
void DebuggerPluginPrivate::attachExternalApplication()
{
AttachExternalDialog dlg(mainWindow());
dlg.setProfileIndex(configValue(_("LastAttachExternalProfileIndex")).toInt());
if (dlg.exec() != QDialog::Accepted)
return;
if (dlg.attachPID() == 0) {
QMessageBox::warning(mainWindow(), tr("Warning"),
tr("Cannot attach to process with PID 0"));
return;
}
setConfigValue(_("LastAttachExternalProfileIndex"), QVariant(dlg.profileIndex()));
DebuggerStartParameters sp;
fillParameters(&sp, dlg.profileId());
sp.attachPID = dlg.attachPID();
sp.displayName = tr("Process %1").arg(dlg.attachPID());
sp.executable = dlg.executable();
sp.startMode = AttachExternal;
sp.closeMode = DetachAtClose;
if (DebuggerRunControl *rc = createDebugger(sp))
startDebugger(rc);
}
//void DebuggerPluginPrivate::attachToLocalProcessHelper()
//{
// AttachExternalDialog dlg(mainWindow());
// dlg.setProfileIndex(configValue(_("LastAttachExternalProfileIndex")).toInt());
// if (dlg.exec() != QDialog::Accepted)
// return;
// if (dlg.attachPID() == 0) {
// QMessageBox::warning(mainWindow(), tr("Warning"),
// tr("Cannot attach to process with PID 0"));
// return;
// }
// setConfigValue(_("LastAttachExternalProfileIndex"), QVariant(dlg.profileIndex()));
// DebuggerStartParameters sp;
// fillParameters(&sp, dlg.profileId());
// sp.attachPID = dlg.attachPID();
// sp.displayName = tr("Process %1").arg(dlg.attachPID());
// sp.executable = dlg.executable();
// sp.startMode = AttachExternal;
// sp.closeMode = DetachAtClose;
// if (DebuggerRunControl *rc = createDebugger(sp))
// startDebugger(rc);
//}
void DebuggerPluginPrivate::attachExternalApplication(RunControl *rc)
{
DebuggerStartParameters sp;
fillParameters(&sp, ProfileManager::instance()->defaultProfile()->id()); // FIXME: Extract from rc.
sp.attachPID = rc->applicationProcessHandle().pid();
sp.displayName = tr("Debugger attached to %1").arg(rc->displayName());
sp.startMode = AttachExternal;
sp.closeMode = DetachAtClose;
if (DebuggerRunControl *rc = createDebugger(sp))
startDebugger(rc);
}
//void DebuggerPluginPrivate::attachToLocalProcess(RunControl *rc)
//{
// DebuggerStartParameters sp;
// fillParameters(&sp, ProfileManager::instance()->defaultProfile()->id()); // FIXME: Extract from rc.
// sp.attachPID = rc->applicationProcessHandle().pid();
// sp.displayName = tr("Debugger attached to %1").arg(rc->displayName());
// sp.startMode = AttachExternal;
// sp.closeMode = DetachAtClose;
// if (DebuggerRunControl *rc = createDebugger(sp))
// startDebugger(rc);
//}
void DebuggerPluginPrivate::attachCore()
{
......@@ -1637,11 +1637,6 @@ void DebuggerPluginPrivate::startRemoteCdbSession()
startDebugger(rc);
}
//bool DebuggerPluginPrivate::queryRemoteParameters(DebuggerStartParameters &sp, bool useScript)
//{
// return StartRemoteDialog::run(mainWindow(), m_coreSettings, useScript, &sp);
//}
void DebuggerPluginPrivate::startRemoteProcess()
{
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()
{
StartGdbServerDialog dlg(mainWindow());
dlg.startGdbServer();
attachToProcess(true);
}
void DebuggerPluginPrivate::attachToRunningApplication()
{
StartGdbServerDialog dlg(mainWindow());
dlg.attachToRemoteProcess();
attachToProcess(false);
}
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()
......@@ -2137,7 +2184,6 @@ void DebuggerPluginPrivate::setInitialState()
m_toolTipManager->closeAllToolTips();
m_startLocalProcessAction->setEnabled(true);
m_attachToLocalProcessAction->setEnabled(true);
m_attachToQmlPortAction->setEnabled(true);
m_attachToCoreAction->setEnabled(true);
m_startRemoteProcessAction->setEnabled(true);
......@@ -2264,7 +2310,6 @@ void DebuggerPluginPrivate::updateState(DebuggerEngine *engine)
m_startLocalProcessAction->setEnabled(true);