ProjectExplorer/Debugger/RL: Make run control state transitions more uniform

Forward all tool and target activities to run control, and initiate
further state transitions (only) from there. Also, make sure
tool/target's on finished() triggered on all finishing code paths.

After that, the base state handling is sufficient to handle remote linux
running and debugging.

Change-Id: I0150ef249c9ad0b7b8ac7192be6dc860c9ca8fc5
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
hjk
2017-05-04 12:12:27 +02:00
parent ac59e2be40
commit 2a46b1521d
14 changed files with 385 additions and 500 deletions

View File

@@ -25,10 +25,10 @@
#include "remotelinuxdebugsupport.h"
#include "remotelinuxcustomrunconfiguration.h"
#include "remotelinuxrunconfiguration.h"
#include <debugger/debuggerruncontrol.h>
#include <debugger/debuggerstartparameters.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/project.h>
@@ -41,109 +41,87 @@
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <QPointer>
using namespace QSsh;
using namespace Debugger;
using namespace ProjectExplorer;
using namespace Utils;
namespace RemoteLinux {
namespace Internal {
class LinuxDeviceDebugSupportPrivate
LinuxDeviceDebugSupport::LinuxDeviceDebugSupport(RunControl *runControl, QString *errorMessage)
: DebuggerRunTool(runControl)
{
public:
QByteArray gdbserverOutput;
Port gdbServerPort;
Port qmlPort;
};
RunConfiguration *runConfig = runControl->runConfiguration();
} // namespace Internal
QString symbolFile;
if (auto rlrc = qobject_cast<RemoteLinuxRunConfiguration *>(runConfig))
symbolFile = rlrc->localExecutableFilePath();
if (auto rlrc = qobject_cast<Internal::RemoteLinuxCustomRunConfiguration *>(runConfig))
symbolFile = rlrc->localExecutableFilePath();
if (symbolFile.isEmpty()) {
*errorMessage = tr("Cannot debug: Local executable is not set.");
return;
}
using namespace Internal;
DebuggerStartParameters params;
params.startMode = AttachToRemoteServer;
params.closeMode = KillAndExitMonitorAtClose;
params.remoteSetupNeeded = false;
LinuxDeviceDebugSupport::LinuxDeviceDebugSupport(RunControl *runControl,
const Debugger::DebuggerStartParameters &sp,
QString *errorMessage)
: DebuggerRunTool(runControl, sp, errorMessage),
d(new LinuxDeviceDebugSupportPrivate)
{
connect(this, &DebuggerRunTool::requestRemoteSetup,
this, &LinuxDeviceDebugSupport::handleRemoteSetupRequested);
connect(runControl, &RunControl::finished,
this, &LinuxDeviceDebugSupport::handleDebuggingFinished);
if (isQmlDebugging()) {
params.qmlServer.host = device()->sshParameters().host;
params.qmlServer.port = Utils::Port(); // port is selected later on
}
if (isCppDebugging()) {
Runnable r = runnable();
QTC_ASSERT(r.is<StandardRunnable>(), return);
auto stdRunnable = r.as<StandardRunnable>();
params.useExtendedRemote = true;
params.inferior.executable = stdRunnable.executable;
params.inferior.commandLineArguments = stdRunnable.commandLineArguments;
if (isQmlDebugging()) {
params.inferior.commandLineArguments.prepend(' ');
params.inferior.commandLineArguments.prepend(
QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlDebuggerServices));
}
params.remoteChannel = device()->sshParameters().host + ":-1";
params.symbolFile = symbolFile;
}
connect(qobject_cast<AbstractRemoteLinuxRunSupport *>(runControl->targetRunner()),
&AbstractRemoteLinuxRunSupport::executionStartRequested,
this, &LinuxDeviceDebugSupport::startExecution);
connect(qobject_cast<AbstractRemoteLinuxRunSupport *>(runControl->targetRunner()),
&AbstractRemoteLinuxRunSupport::adapterSetupFailed,
this, &LinuxDeviceDebugSupport::handleAdapterSetupFailed);
setStartParameters(params);
}
LinuxDeviceDebugSupport::~LinuxDeviceDebugSupport()
{
delete d;
}
AbstractRemoteLinuxRunSupport *LinuxDeviceDebugSupport::targetRunner() const
void LinuxDeviceDebugSupport::prepare()
{
return qobject_cast<AbstractRemoteLinuxRunSupport *>(runControl()->targetRunner());
}
AbstractRemoteLinuxRunSupport::State LinuxDeviceDebugSupport::state() const
{
AbstractRemoteLinuxRunSupport *runner = targetRunner();
return runner ? runner->state() : AbstractRemoteLinuxRunSupport::Inactive;
}
void LinuxDeviceDebugSupport::handleRemoteSetupRequested()
{
QTC_ASSERT(state() == AbstractRemoteLinuxRunSupport::Inactive, return);
showMessage(tr("Checking available ports...") + QLatin1Char('\n'), LogStatus);
targetRunner()->startPortsGathering();
}
void LinuxDeviceDebugSupport::startExecution()
{
QTC_ASSERT(state() == AbstractRemoteLinuxRunSupport::GatheringResources, return);
auto targetRunner = qobject_cast<AbstractRemoteLinuxRunSupport *>(runControl()->targetRunner());
if (isCppDebugging()) {
d->gdbServerPort = targetRunner()->findPort();
if (!d->gdbServerPort.isValid()) {
handleAdapterSetupFailed(tr("Not enough free ports on device for C++ debugging."));
m_gdbServerPort = targetRunner->findPort();
if (!m_gdbServerPort.isValid()) {
reportFailure(tr("Not enough free ports on device for C++ debugging."));
return;
}
}
if (isQmlDebugging()) {
d->qmlPort = targetRunner()->findPort();
if (!d->qmlPort.isValid()) {
handleAdapterSetupFailed(tr("Not enough free ports on device for QML debugging."));
m_qmlPort = targetRunner->findPort();
if (!m_qmlPort.isValid()) {
reportFailure(tr("Not enough free ports on device for QML debugging."));
return;
}
}
targetRunner()->setState(AbstractRemoteLinuxRunSupport::StartingRunner);
d->gdbserverOutput.clear();
runControl()->setRunnable(realRunnable());
ApplicationLauncher *launcher = targetRunner()->applicationLauncher();
connect(launcher, &ApplicationLauncher::remoteStderr,
this, &LinuxDeviceDebugSupport::handleRemoteErrorOutput);
connect(launcher, &ApplicationLauncher::remoteStdout,
this, &LinuxDeviceDebugSupport::handleRemoteOutput);
connect(launcher, &ApplicationLauncher::finished,
this, &LinuxDeviceDebugSupport::handleAppRunnerFinished);
connect(launcher, &ApplicationLauncher::reportProgress,
this, &LinuxDeviceDebugSupport::handleProgressReport);
connect(launcher, &ApplicationLauncher::reportError,
this, &LinuxDeviceDebugSupport::handleAppRunnerError);
if (isQmlDebugging() && !isCppDebugging())
connect(launcher, &ApplicationLauncher::remoteProcessStarted,
this, &LinuxDeviceDebugSupport::handleRemoteProcessStarted);
RemoteSetupResult result;
result.success = true;
result.gdbServerPort = m_gdbServerPort;
result.qmlServerPort = m_qmlPort;
setRemoteParameters(result);
launcher->start(realRunnable(), device());
DebuggerRunTool::prepare();
}
Runnable LinuxDeviceDebugSupport::realRunnable() const
@@ -153,7 +131,7 @@ Runnable LinuxDeviceDebugSupport::realRunnable() const
QString command;
if (isQmlDebugging())
args.prepend(QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlDebuggerServices, d->qmlPort));
args.prepend(QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlDebuggerServices, m_qmlPort));
if (isQmlDebugging() && !isCppDebugging()) {
command = r.executable;
@@ -163,102 +141,11 @@ Runnable LinuxDeviceDebugSupport::realRunnable() const
command = QLatin1String("gdbserver");
args.clear();
args.append(QString::fromLatin1("--multi"));
args.append(QString::fromLatin1(":%1").arg(d->gdbServerPort.number()));
args.append(QString::fromLatin1(":%1").arg(m_gdbServerPort.number()));
}
r.executable = command;
r.commandLineArguments = QtcProcess::joinArgs(args, OsTypeLinux);
return r;
}
void LinuxDeviceDebugSupport::handleAppRunnerError(const QString &error)
{
if (state() == AbstractRemoteLinuxRunSupport::Running) {
showMessage(error, AppError);
notifyInferiorIll();
} else if (state() != AbstractRemoteLinuxRunSupport::Inactive) {
handleAdapterSetupFailed(error);
}
}
void LinuxDeviceDebugSupport::handleAppRunnerFinished(bool success)
{
if (state() == AbstractRemoteLinuxRunSupport::Inactive)
return;
if (state() == AbstractRemoteLinuxRunSupport::Running) {
// The QML engine does not realize on its own that the application has finished.
if (isQmlDebugging() && !isCppDebugging())
quitDebugger();
else if (!success)
notifyInferiorIll();
} else if (state() == AbstractRemoteLinuxRunSupport::StartingRunner) {
RemoteSetupResult result;
result.success = false;
result.reason = tr("Debugging failed.");
notifyEngineRemoteSetupFinished(result);
}
targetRunner()->reset();
}
void LinuxDeviceDebugSupport::handleDebuggingFinished()
{
targetRunner()->setFinished();
targetRunner()->reset();
}
void LinuxDeviceDebugSupport::handleRemoteOutput(const QByteArray &output)
{
QTC_ASSERT(state() == AbstractRemoteLinuxRunSupport::Inactive
|| state() == AbstractRemoteLinuxRunSupport::Running, return);
showMessage(QString::fromUtf8(output), AppOutput);
}
void LinuxDeviceDebugSupport::handleRemoteErrorOutput(const QByteArray &output)
{
QTC_ASSERT(state() != AbstractRemoteLinuxRunSupport::GatheringResources, return);
showMessage(QString::fromUtf8(output), AppError);
if (state() == AbstractRemoteLinuxRunSupport::StartingRunner && isCppDebugging()) {
d->gdbserverOutput += output;
if (d->gdbserverOutput.contains("Listening on port")) {
handleAdapterSetupDone();
d->gdbserverOutput.clear();
}
}
}
void LinuxDeviceDebugSupport::handleProgressReport(const QString &progressOutput)
{
showMessage(progressOutput + QLatin1Char('\n'), LogStatus);
}
void LinuxDeviceDebugSupport::handleAdapterSetupFailed(const QString &error)
{
RemoteSetupResult result;
result.success = false;
result.reason = tr("Initial setup failed: %1").arg(error);
notifyEngineRemoteSetupFinished(result);
}
void LinuxDeviceDebugSupport::handleAdapterSetupDone()
{
targetRunner()->setState(AbstractRemoteLinuxRunSupport::Running);
RemoteSetupResult result;
result.success = true;
result.gdbServerPort = d->gdbServerPort;
result.qmlServerPort = d->qmlPort;
notifyEngineRemoteSetupFinished(result);
}
void LinuxDeviceDebugSupport::handleRemoteProcessStarted()
{
QTC_ASSERT(isQmlDebugging() && !isCppDebugging(), return);
QTC_ASSERT(state() == AbstractRemoteLinuxRunSupport::StartingRunner, return);
handleAdapterSetupDone();
}
} // namespace RemoteLinux