Android: Setup QML debugging

Change-Id: Ida96411f9710bd9a968d4b934fb50f69bf3666fc
Reviewed-by: Kai Koehne <kai.koehne@digia.com>
Reviewed-by: Aurindam Jana <aurindam.jana@digia.com>
This commit is contained in:
Aurindam Jana
2013-04-18 10:01:05 +02:00
parent 714298b370
commit e8c23e1d71
7 changed files with 71 additions and 21 deletions

View File

@@ -49,6 +49,7 @@
#include <qtsupport/qtkitinformation.h> #include <qtsupport/qtkitinformation.h>
#include <QDir> #include <QDir>
#include <QTcpServer>
using namespace Debugger; using namespace Debugger;
using namespace ProjectExplorer; using namespace ProjectExplorer;
@@ -113,8 +114,11 @@ RunControl *AndroidDebugSupport::createDebugRunControl(AndroidRunConfiguration *
} }
if (aspect->useQmlDebugger()) { if (aspect->useQmlDebugger()) {
params.languages |= QmlLanguage; params.languages |= QmlLanguage;
params.qmlServerAddress = QLatin1String("localhost"); QTcpServer server;
params.qmlServerPort = aspect->qmlDebugServerPort(); QTC_ASSERT(server.listen(QHostAddress::LocalHost)
|| server.listen(QHostAddress::LocalHostIPv6), return 0);
params.qmlServerAddress = server.serverAddress().toString();
params.remoteSetupNeeded = true;
//TODO: Not sure if these are the right paths. //TODO: Not sure if these are the right paths.
params.projectSourceDirectory = project->projectDirectory(); params.projectSourceDirectory = project->projectDirectory();
params.projectSourceFiles = project->files(Qt4Project::ExcludeGeneratedFiles); params.projectSourceFiles = project->files(Qt4Project::ExcludeGeneratedFiles);
@@ -141,7 +145,7 @@ AndroidDebugSupport::AndroidDebugSupport(AndroidRunConfiguration *runConfig,
connect(m_runControl, SIGNAL(finished()), connect(m_runControl, SIGNAL(finished()),
m_runner, SLOT(stop())); m_runner, SLOT(stop()));
connect(m_runControl->engine(), SIGNAL(aboutToNotifyInferiorSetupOk()), connect(m_runControl->engine(), SIGNAL(aboutToNotifyInferiorSetupOk()),
m_runner, SLOT(handleGdbRunning())); m_runner, SLOT(handleRemoteDebuggerRunning()));
connect(m_runner, SIGNAL(remoteServerRunning(QByteArray,int)), connect(m_runner, SIGNAL(remoteServerRunning(QByteArray,int)),
SLOT(handleRemoteServerRunning(QByteArray,int))); SLOT(handleRemoteServerRunning(QByteArray,int)));

View File

@@ -84,6 +84,11 @@ void AndroidDevice::executeAction(Core::Id actionId, QWidget *parent) const
Q_UNUSED(parent) Q_UNUSED(parent)
} }
bool AndroidDevice::canAutoDetectPorts() const
{
return true;
}
IDevice::Ptr AndroidDevice::clone() const IDevice::Ptr AndroidDevice::clone() const
{ {
return IDevice::Ptr(new AndroidDevice(*this)); return IDevice::Ptr(new AndroidDevice(*this));

View File

@@ -46,6 +46,7 @@ public:
QList<Core::Id> actionIds() const; QList<Core::Id> actionIds() const;
QString displayNameForActionId(Core::Id actionId) const; QString displayNameForActionId(Core::Id actionId) const;
void executeAction(Core::Id actionId, QWidget *parent = 0) const; void executeAction(Core::Id actionId, QWidget *parent = 0) const;
bool canAutoDetectPorts() const;
ProjectExplorer::IDevice::Ptr clone() const; ProjectExplorer::IDevice::Ptr clone() const;

View File

@@ -42,6 +42,7 @@
#include <QTime> #include <QTime>
#include <QtConcurrentRun> #include <QtConcurrentRun>
#include <QTemporaryFile> #include <QTemporaryFile>
#include <QTcpServer>
namespace Android { namespace Android {
namespace Internal { namespace Internal {
@@ -60,7 +61,13 @@ AndroidRunner::AndroidRunner(QObject *parent, AndroidRunConfiguration *runConfig
QTC_CHECK(channel.startsWith(QLatin1Char(':'))); QTC_CHECK(channel.startsWith(QLatin1Char(':')));
m_localGdbServerPort = channel.mid(1).toUShort(); m_localGdbServerPort = channel.mid(1).toUShort();
QTC_CHECK(m_localGdbServerPort); QTC_CHECK(m_localGdbServerPort);
m_qmlPort = aspect->qmlDebugServerPort(); if (m_useQmlDebugger) {
QTcpServer server;
QTC_ASSERT(server.listen(QHostAddress::LocalHost)
|| server.listen(QHostAddress::LocalHostIPv6),
qDebug() << tr("No free ports available on host for QML debugging."));
m_qmlPort = server.serverPort();
}
ProjectExplorer::Target *target = runConfig->target(); ProjectExplorer::Target *target = runConfig->target();
AndroidDeployStep *ds = runConfig->deployStep(); AndroidDeployStep *ds = runConfig->deployStep();
if ((m_useLocalQtLibs = ds->useLocalQtLibs())) { if ((m_useLocalQtLibs = ds->useLocalQtLibs())) {
@@ -81,7 +88,6 @@ AndroidRunner::AndroidRunner(QObject *parent, AndroidRunConfiguration *runConfig
m_gdbserverSocket = packageDir + _("/debug-socket"); m_gdbserverSocket = packageDir + _("/debug-socket");
m_gdbserverPath = packageDir + _("/lib/gdbserver"); m_gdbserverPath = packageDir + _("/lib/gdbserver");
m_gdbserverCommand = m_gdbserverPath + _(" --multi +") + m_gdbserverSocket; m_gdbserverCommand = m_gdbserverPath + _(" --multi +") + m_gdbserverSocket;
// Detect busybox, as we need to pass -w to ps to get wide output. // Detect busybox, as we need to pass -w to ps to get wide output.
QProcess psProc; QProcess psProc;
psProc.start(m_adb, selector() << _("shell") << _("readlink") << _("$(which ps)")); psProc.start(m_adb, selector() << _("shell") << _("readlink") << _("$(which ps)"));
@@ -216,7 +222,7 @@ void AndroidRunner::asyncStart()
} }
if (m_useQmlDebugger) { if (m_useQmlDebugger) {
// currently forward to same port on device and host // currently forward to same port on device and host
QString port = QString::fromLatin1("tcp:%1").arg(m_qmlPort); const QString port = QString::fromLatin1("tcp:%1").arg(m_qmlPort);
QProcess adb; QProcess adb;
adb.start(m_adb, selector() << _("forward") << port << port); adb.start(m_adb, selector() << _("forward") << port << port);
if (!adb.waitForStarted()) { if (!adb.waitForStarted()) {
@@ -228,9 +234,8 @@ void AndroidRunner::asyncStart()
return; return;
} }
args << _("-e") << _("qml_debug") << _("true"); args << _("-e") << _("qml_debug") << _("true");
args << _("-e") << _("qmljsdebugger") << QString::fromLatin1("port:%1").arg(m_qmlPort); args << _("-e") << _("qmljsdebugger") << QString::fromLatin1("port:%1,block").arg(m_qmlPort);
} }
if (m_useLocalQtLibs) { if (m_useLocalQtLibs) {
args << _("-e") << _("use_local_qt_libs") << _("true"); args << _("-e") << _("use_local_qt_libs") << _("true");
args << _("-e") << _("libs_prefix") << _("/data/local/tmp/qt/"); args << _("-e") << _("libs_prefix") << _("/data/local/tmp/qt/");
@@ -252,7 +257,7 @@ void AndroidRunner::asyncStart()
return; return;
} }
if (m_useCppDebugger || m_useQmlDebugger) { if (m_useCppDebugger) {
// Handling ping. // Handling ping.
for (int i = 0; ; ++i) { for (int i = 0; ; ++i) {
@@ -289,19 +294,25 @@ void AndroidRunner::asyncStart()
} }
m_wasStarted = true; m_wasStarted = true;
if (m_useCppDebugger || m_useQmlDebugger) { if (m_useCppDebugger) {
// This will be funneled to the engine to actually start and attach // This will be funneled to the engine to actually start and attach
// gdb. Afterwards this ends up in handleGdbRunning() below. // gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
QByteArray serverChannel = ':' + QByteArray::number(m_localGdbServerPort); QByteArray serverChannel = ':' + QByteArray::number(m_localGdbServerPort);
emit remoteServerRunning(serverChannel, m_processPID); emit remoteServerRunning(serverChannel, m_processPID);
} else if (m_useQmlDebugger) {
// This will be funneled to the engine to actually start and attach
// gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
QByteArray serverChannel = QByteArray::number(m_qmlPort);
emit remoteServerRunning(serverChannel, m_processPID);
} else { } else {
// Start without debugging. // Start without debugging.
emit remoteProcessStarted(-1, -1); emit remoteProcessStarted(-1, -1);
} }
} }
void AndroidRunner::handleGdbRunning() void AndroidRunner::handleRemoteDebuggerRunning()
{ {
if (m_useCppDebugger) {
QTemporaryFile tmp(_("pingpong")); QTemporaryFile tmp(_("pingpong"));
tmp.open(); tmp.open();
@@ -310,7 +321,8 @@ void AndroidRunner::handleGdbRunning()
process.waitForFinished(); process.waitForFinished();
QTC_CHECK(m_processPID != -1); QTC_CHECK(m_processPID != -1);
emit remoteProcessStarted(m_localGdbServerPort, -1); }
emit remoteProcessStarted(m_localGdbServerPort, m_qmlPort);
} }
void AndroidRunner::stop() void AndroidRunner::stop()

View File

@@ -56,7 +56,7 @@ public:
public slots: public slots:
void start(); void start();
void stop(); void stop();
void handleGdbRunning(); void handleRemoteDebuggerRunning();
signals: signals:
void remoteServerRunning(const QByteArray &serverChannel, int pid); void remoteServerRunning(const QByteArray &serverChannel, int pid);
@@ -93,7 +93,7 @@ private:
bool m_useCppDebugger; bool m_useCppDebugger;
bool m_useQmlDebugger; bool m_useQmlDebugger;
ushort m_localGdbServerPort; // Local end of forwarded debug socket. ushort m_localGdbServerPort; // Local end of forwarded debug socket.
uint m_qmlPort; quint16 m_qmlPort;
bool m_useLocalQtLibs; bool m_useLocalQtLibs;
QString m_pingFile; QString m_pingFile;
QString m_pongFile; QString m_pongFile;

View File

@@ -345,6 +345,12 @@ QmlEngine::~QmlEngine()
Core::EditorManager::instance()->closeEditors(editorsToClose); Core::EditorManager::instance()->closeEditors(editorsToClose);
} }
void QmlEngine::notifyInferiorSetupOk()
{
emit aboutToNotifyInferiorSetupOk();
DebuggerEngine::notifyInferiorSetupOk();
}
void QmlEngine::setupInferior() void QmlEngine::setupInferior()
{ {
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
@@ -620,6 +626,25 @@ void QmlEngine::notifyEngineRemoteSetupFailed(const QString &message)
notifyEngineSetupFailed(); notifyEngineSetupFailed();
} }
void QmlEngine::notifyEngineRemoteServerRunning(const QByteArray &serverChannel, int pid)
{
bool ok = false;
quint16 qmlPort = serverChannel.toUInt(&ok);
if (ok)
startParameters().qmlServerPort = qmlPort;
else
qWarning() << tr("QML debugging port not set! Unable to convert %1 to unsigned int.").arg(QString::fromLatin1(serverChannel));
DebuggerEngine::notifyEngineRemoteServerRunning(serverChannel, pid);
notifyEngineSetupOk();
// The remote setup can take a while especially with mixed debugging.
// Just waiting for 8 seconds is not enough. Increase the timeout
// to 60 s
// In case we get an output the m_outputParser will start the connection.
m_noDebugOutputTimer.setInterval(60000);
}
void QmlEngine::shutdownInferior() void QmlEngine::shutdownInferior()
{ {
if (m_adapter.activeDebuggerClient()) if (m_adapter.activeDebuggerClient())

View File

@@ -61,8 +61,10 @@ public:
DebuggerEngine *masterEngine = 0); DebuggerEngine *masterEngine = 0);
~QmlEngine(); ~QmlEngine();
void notifyInferiorSetupOk();
void notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort); void notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort);
void notifyEngineRemoteSetupFailed(const QString &message); void notifyEngineRemoteSetupFailed(const QString &message);
void notifyEngineRemoteServerRunning(const QByteArray &, int pid);
bool canDisplayTooltip() const; bool canDisplayTooltip() const;
@@ -90,6 +92,7 @@ public:
signals: signals:
void tooltipRequested(const QPoint &mousePos, void tooltipRequested(const QPoint &mousePos,
TextEditor::ITextEditor *editor, int cursorPos); TextEditor::ITextEditor *editor, int cursorPos);
void aboutToNotifyInferiorSetupOk();
private slots: private slots:
void disconnected(); void disconnected();