2013-09-20 16:48:01 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2014 Petar Perisin <petar.perisin@gmail.com>
|
|
|
|
|
** Contact: http://www.qt-project.org/legal
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator.
|
|
|
|
|
**
|
|
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and Digia. For licensing terms and
|
|
|
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
|
|
|
** use the contact form at http://qt.digia.com/contact-us.
|
|
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
|
** General Public License version 2.1 as published by the Free Software
|
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
|
**
|
|
|
|
|
** In addition, as a special exception, Digia gives you certain additional
|
|
|
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
|
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "unstartedappwatcherdialog.h"
|
|
|
|
|
|
|
|
|
|
#include "debuggerdialogs.h"
|
|
|
|
|
|
|
|
|
|
#include <utils/pathchooser.h>
|
|
|
|
|
|
|
|
|
|
#include <projectexplorer/kit.h>
|
|
|
|
|
#include <projectexplorer/target.h>
|
|
|
|
|
#include <projectexplorer/project.h>
|
|
|
|
|
#include <projectexplorer/kitmanager.h>
|
|
|
|
|
#include <projectexplorer/projectexplorer.h>
|
|
|
|
|
#include <projectexplorer/runconfiguration.h>
|
|
|
|
|
#include <projectexplorer/buildconfiguration.h>
|
|
|
|
|
#include <projectexplorer/localapplicationrunconfiguration.h>
|
|
|
|
|
|
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
|
#include <QHBoxLayout>
|
|
|
|
|
#include <QPushButton>
|
|
|
|
|
#include <QCheckBox>
|
|
|
|
|
#include <QLabel>
|
|
|
|
|
#include <QFormLayout>
|
|
|
|
|
#include <QLineEdit>
|
|
|
|
|
#include <QFileDialog>
|
|
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
|
2013-09-20 16:48:01 +02:00
|
|
|
namespace Debugger {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
/*!
|
|
|
|
|
\class Debugger::Internal::UnstartedAppWatcherDialog
|
|
|
|
|
|
|
|
|
|
\brief The UnstartedAppWatcherDialog class provides ability to wait for a certain application
|
|
|
|
|
to be started, after what it will attach to it.
|
|
|
|
|
|
|
|
|
|
This dialog can be useful in cases where automated scripts are used in order to execute some
|
|
|
|
|
tests on application. In those cases application will be started from a script. This dialog
|
|
|
|
|
allows user to attach to application in those cases in very short time after they are started.
|
|
|
|
|
|
|
|
|
|
In order to attach, user needs to provide appropriate kit (for local debugging) and
|
|
|
|
|
application path.
|
|
|
|
|
|
|
|
|
|
After selecting start, dialog will check if selected application is started every
|
|
|
|
|
10 miliseconds. As soon as application is started, QtCreator will attach to it.
|
|
|
|
|
|
|
|
|
|
After user attaches, it is possible to keep dialog active and as soon as debugging
|
|
|
|
|
session ends, it will start watching again. This is because sometimes automated test
|
|
|
|
|
scripts can restart application several times during tests.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
UnstartedAppWatcherDialog::UnstartedAppWatcherDialog(QWidget *parent)
|
|
|
|
|
: QDialog(parent)
|
2013-09-20 16:48:01 +02:00
|
|
|
{
|
2014-03-06 14:44:03 +01:00
|
|
|
setWindowTitle(tr("Attach to Process Not Yet Started"));
|
2013-09-20 16:48:01 +02:00
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
m_kitChooser = new DebuggerKitChooser(DebuggerKitChooser::LocalDebugging, this);
|
|
|
|
|
m_kitChooser->populate();
|
|
|
|
|
m_kitChooser->setVisible(true);
|
2013-09-20 16:48:01 +02:00
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
Project *project = ProjectExplorerPlugin::currentProject();
|
2013-09-20 16:48:01 +02:00
|
|
|
if (project && project->activeTarget() && project->activeTarget()->kit())
|
2014-02-12 18:23:33 +01:00
|
|
|
m_kitChooser->setCurrentKitId(project->activeTarget()->kit()->id());
|
|
|
|
|
else if (KitManager::defaultKit())
|
|
|
|
|
m_kitChooser->setCurrentKitId(KitManager::defaultKit()->id());
|
2013-09-20 16:48:01 +02:00
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
m_pathChooser = new Utils::PathChooser(this);
|
|
|
|
|
m_pathChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
|
|
|
|
|
m_pathChooser->setHistoryCompleter(QLatin1String("LocalExecutable"));
|
2013-09-20 16:48:01 +02:00
|
|
|
|
|
|
|
|
if (project && project->activeTarget() && project->activeTarget()->activeRunConfiguration()) {
|
2014-02-12 18:23:33 +01:00
|
|
|
LocalApplicationRunConfiguration *localAppRC =
|
|
|
|
|
qobject_cast<LocalApplicationRunConfiguration *>
|
2013-09-20 16:48:01 +02:00
|
|
|
(project->activeTarget()->activeRunConfiguration());
|
|
|
|
|
|
|
|
|
|
if (localAppRC)
|
2014-02-12 18:23:33 +01:00
|
|
|
m_pathChooser->setPath(localAppRC->executable());
|
2013-09-20 16:48:01 +02:00
|
|
|
}
|
|
|
|
|
|
2014-03-06 14:44:03 +01:00
|
|
|
m_hideOnAttachCheckBox = new QCheckBox(tr("Hide after attach"), this);
|
|
|
|
|
m_hideOnAttachCheckBox->setToolTip(tr("Hides the dialog after attach "
|
|
|
|
|
" and shows it after application ends.\nAlternatively, closes the dialog."));
|
2013-09-20 16:48:01 +02:00
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
m_hideOnAttachCheckBox->setChecked(false);
|
|
|
|
|
m_hideOnAttachCheckBox->setVisible(true);
|
2013-09-20 16:48:01 +02:00
|
|
|
|
2014-03-06 14:44:03 +01:00
|
|
|
m_continueOnAttachCheckBox = new QCheckBox(tr("Continue on attach"), this);
|
|
|
|
|
m_continueOnAttachCheckBox->setToolTip(tr("Debugger does not stop the"
|
|
|
|
|
" application after attach."));
|
2013-09-20 16:48:01 +02:00
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
m_continueOnAttachCheckBox->setChecked(true);
|
|
|
|
|
m_continueOnAttachCheckBox->setVisible(true);
|
2013-09-20 16:48:01 +02:00
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
m_waitingLabel = new QLabel(QString(), this);
|
|
|
|
|
m_waitingLabel->setAlignment(Qt::AlignCenter);
|
2013-09-20 16:48:01 +02:00
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
m_watchingPushButton = new QPushButton(this);
|
|
|
|
|
m_watchingPushButton->setCheckable(true);
|
|
|
|
|
m_watchingPushButton->setChecked(false);
|
|
|
|
|
m_watchingPushButton->setEnabled(false);
|
|
|
|
|
m_watchingPushButton->setText(tr("Start Watching"));
|
2013-09-20 16:48:01 +02:00
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
m_closePushButton = new QPushButton(this);
|
|
|
|
|
m_closePushButton->setText(tr("Close"));
|
2013-09-20 16:48:01 +02:00
|
|
|
|
|
|
|
|
QHBoxLayout *buttonsLine = new QHBoxLayout();
|
|
|
|
|
buttonsLine->addSpacerItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
|
2014-02-12 18:23:33 +01:00
|
|
|
buttonsLine->addWidget(m_watchingPushButton);
|
|
|
|
|
buttonsLine->addWidget(m_closePushButton);
|
|
|
|
|
|
|
|
|
|
QFormLayout *mainLayout = new QFormLayout(this);
|
|
|
|
|
mainLayout->addRow(new QLabel(tr("Kit: "), this), m_kitChooser);
|
|
|
|
|
mainLayout->addRow(new QLabel(tr("Executable: "), this), m_pathChooser);
|
|
|
|
|
mainLayout->addRow(m_hideOnAttachCheckBox);
|
|
|
|
|
mainLayout->addRow(m_continueOnAttachCheckBox);
|
|
|
|
|
mainLayout->addRow(m_waitingLabel);
|
|
|
|
|
mainLayout->addItem(new QSpacerItem(20, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
|
|
|
|
|
mainLayout->addRow(buttonsLine);
|
|
|
|
|
setLayout(mainLayout);
|
2013-09-20 16:48:01 +02:00
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
connect(m_pathChooser, SIGNAL(beforeBrowsing()), this, SLOT(selectExecutable()));
|
|
|
|
|
connect(m_watchingPushButton, SIGNAL(toggled(bool)), this, SLOT(startStopWatching(bool)));
|
|
|
|
|
connect(m_pathChooser, SIGNAL(pathChanged(QString)), this, SLOT(stopAndCheckExecutable()));
|
|
|
|
|
connect(m_closePushButton, SIGNAL(clicked()), this, SLOT(reject()));
|
2013-09-20 16:48:01 +02:00
|
|
|
connect(&m_timer, SIGNAL(timeout()), this, SLOT(findProcess()));
|
|
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
setWaitingState(checkExecutableString() ? NotWatchingState : InvalidWacherState);
|
2013-09-20 16:48:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnstartedAppWatcherDialog::selectExecutable()
|
|
|
|
|
{
|
|
|
|
|
QString path;
|
|
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
Project *project = ProjectExplorerPlugin::currentProject();
|
2013-09-20 16:48:01 +02:00
|
|
|
|
|
|
|
|
if (project && project->activeTarget() && project->activeTarget()->activeRunConfiguration()) {
|
|
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
LocalApplicationRunConfiguration *localAppRC =
|
|
|
|
|
qobject_cast<LocalApplicationRunConfiguration *>
|
2013-09-20 16:48:01 +02:00
|
|
|
(project->activeTarget()->activeRunConfiguration());
|
|
|
|
|
if (localAppRC)
|
|
|
|
|
path = QFileInfo(localAppRC->executable()).path();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (path.isEmpty()) {
|
|
|
|
|
if (project && project->activeTarget() &&
|
|
|
|
|
project->activeTarget()->activeBuildConfiguration()) {
|
|
|
|
|
path = project->activeTarget()->activeBuildConfiguration()->buildDirectory().toString();
|
|
|
|
|
} else if (project) {
|
|
|
|
|
path = project->projectDirectory();
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-02-12 18:23:33 +01:00
|
|
|
m_pathChooser->setInitialBrowsePathBackup(path);
|
2013-09-20 16:48:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnstartedAppWatcherDialog::startWatching()
|
|
|
|
|
{
|
|
|
|
|
show();
|
|
|
|
|
if (checkExecutableString()) {
|
2014-02-12 18:23:33 +01:00
|
|
|
setWaitingState(WatchingState);
|
2013-09-20 16:48:01 +02:00
|
|
|
startStopTimer(true);
|
|
|
|
|
} else {
|
2014-02-12 18:23:33 +01:00
|
|
|
setWaitingState(InvalidWacherState);
|
2013-09-20 16:48:01 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
void UnstartedAppWatcherDialog::pidFound(const DeviceProcessItem &p)
|
2013-09-20 16:48:01 +02:00
|
|
|
{
|
2014-02-12 18:23:33 +01:00
|
|
|
setWaitingState(FoundState);
|
2013-09-20 16:48:01 +02:00
|
|
|
startStopTimer(false);
|
|
|
|
|
m_process = p;
|
|
|
|
|
|
|
|
|
|
if (hideOnAttach())
|
|
|
|
|
hide();
|
|
|
|
|
else
|
|
|
|
|
accept();
|
|
|
|
|
|
|
|
|
|
emit processFound();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnstartedAppWatcherDialog::startStopWatching(bool start)
|
|
|
|
|
{
|
2014-02-12 18:23:33 +01:00
|
|
|
setWaitingState(start ? WatchingState : NotWatchingState);
|
|
|
|
|
m_watchingPushButton->setText(start ? QLatin1String("Stop Watching")
|
|
|
|
|
: QLatin1String("Start Watching"));
|
2013-09-20 16:48:01 +02:00
|
|
|
startStopTimer(start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnstartedAppWatcherDialog::startStopTimer(bool start)
|
|
|
|
|
{
|
|
|
|
|
if (start)
|
|
|
|
|
m_timer.start(10);
|
|
|
|
|
else
|
|
|
|
|
m_timer.stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnstartedAppWatcherDialog::findProcess()
|
|
|
|
|
{
|
2014-02-12 18:23:33 +01:00
|
|
|
QString appName = m_pathChooser->path();
|
|
|
|
|
DeviceProcessItem fallback;
|
|
|
|
|
foreach (const DeviceProcessItem &p, DeviceProcessList::localProcesses()) {
|
2013-09-20 16:48:01 +02:00
|
|
|
if (p.exe == appName) {
|
|
|
|
|
pidFound(p);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (p.cmdLine.startsWith(appName))
|
|
|
|
|
fallback = p;
|
|
|
|
|
}
|
|
|
|
|
if (fallback.pid != 0)
|
|
|
|
|
pidFound(fallback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnstartedAppWatcherDialog::stopAndCheckExecutable()
|
|
|
|
|
{
|
|
|
|
|
startStopTimer(false);
|
2014-02-12 18:23:33 +01:00
|
|
|
setWaitingState(checkExecutableString() ? NotWatchingState : InvalidWacherState);
|
2013-09-20 16:48:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UnstartedAppWatcherDialog::checkExecutableString() const
|
|
|
|
|
{
|
2014-02-12 18:23:33 +01:00
|
|
|
if (!m_pathChooser->path().isEmpty()) {
|
|
|
|
|
QFileInfo fileInfo(m_pathChooser->path());
|
2013-09-20 16:48:01 +02:00
|
|
|
return (fileInfo.exists() && fileInfo.isFile());
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
Kit *UnstartedAppWatcherDialog::currentKit() const
|
2013-09-20 16:48:01 +02:00
|
|
|
{
|
2014-02-12 18:23:33 +01:00
|
|
|
return m_kitChooser->currentKit();
|
2013-09-20 16:48:01 +02:00
|
|
|
}
|
|
|
|
|
|
2014-02-12 18:23:33 +01:00
|
|
|
DeviceProcessItem UnstartedAppWatcherDialog::currentProcess() const
|
2013-09-20 16:48:01 +02:00
|
|
|
{
|
|
|
|
|
return m_process;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UnstartedAppWatcherDialog::hideOnAttach() const
|
|
|
|
|
{
|
2014-02-12 18:23:33 +01:00
|
|
|
return m_hideOnAttachCheckBox->isChecked();
|
2013-09-20 16:48:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UnstartedAppWatcherDialog::continueOnAttach() const
|
|
|
|
|
{
|
2014-02-12 18:23:33 +01:00
|
|
|
return m_continueOnAttachCheckBox->isChecked();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnstartedAppWatcherDialog::setWaitingState(UnstartedAppWacherState state)
|
|
|
|
|
{
|
|
|
|
|
switch (state) {
|
|
|
|
|
case InvalidWacherState:
|
2014-03-06 14:44:03 +01:00
|
|
|
m_waitingLabel->setText(tr("Select valid executable."));
|
2014-02-12 18:23:33 +01:00
|
|
|
m_watchingPushButton->setEnabled(false);
|
|
|
|
|
m_watchingPushButton->setChecked(false);
|
|
|
|
|
m_pathChooser->setEnabled(true);
|
|
|
|
|
m_kitChooser->setEnabled(true);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NotWatchingState:
|
2014-03-06 14:44:03 +01:00
|
|
|
m_waitingLabel->setText(tr("Not watching."));
|
2014-02-12 18:23:33 +01:00
|
|
|
m_watchingPushButton->setEnabled(true);
|
|
|
|
|
m_watchingPushButton->setChecked(false);
|
|
|
|
|
m_pathChooser->setEnabled(true);
|
|
|
|
|
m_kitChooser->setEnabled(true);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WatchingState:
|
|
|
|
|
m_waitingLabel->setText(tr("Waiting for process to start..."));
|
|
|
|
|
m_watchingPushButton->setEnabled(true);
|
|
|
|
|
m_watchingPushButton->setChecked(true);
|
|
|
|
|
m_pathChooser->setEnabled(false);
|
|
|
|
|
m_kitChooser->setEnabled(false);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FoundState:
|
|
|
|
|
m_waitingLabel->setText(tr("Attach"));
|
|
|
|
|
m_watchingPushButton->setEnabled(false);
|
|
|
|
|
m_watchingPushButton->setChecked(true);
|
|
|
|
|
m_pathChooser->setEnabled(false);
|
|
|
|
|
m_kitChooser->setEnabled(true);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2013-09-20 16:48:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Debugger
|