forked from qt-creator/qt-creator
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user