Debugging on Android

This implements the host side of
https://codereview.qt-project.org/#change,50290

Change-Id: I13c7df29534a2a85202c2b295b139896443b0120
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com>
Reviewed-by: Daniel Teske <daniel.teske@digia.com>
Reviewed-by: BogDan Vatra <bogdan@kde.org>
Reviewed-by: hjk <hjk121@nokiamail.com>
This commit is contained in:
hjk
2013-02-27 15:12:36 +01:00
parent 63f3a51060
commit 066efcd6a6
9 changed files with 328 additions and 174 deletions

View File

@@ -135,7 +135,11 @@ AndroidDebugSupport::AndroidDebugSupport(AndroidRunConfiguration *runConfig,
m_runner, SLOT(start())); m_runner, SLOT(start()));
connect(m_runControl, SIGNAL(finished()), connect(m_runControl, SIGNAL(finished()),
m_runner, SLOT(stop())); m_runner, SLOT(stop()));
connect(m_runControl->engine(), SIGNAL(aboutToNotifyInferiorSetupOk()),
m_runner, SLOT(handleGdbRunning()));
connect(m_runner, SIGNAL(remoteServerRunning(QByteArray,int)),
SLOT(handleRemoteServerRunning(QByteArray,int)));
connect(m_runner, SIGNAL(remoteProcessStarted(int,int)), connect(m_runner, SIGNAL(remoteProcessStarted(int,int)),
SLOT(handleRemoteProcessStarted(int,int))); SLOT(handleRemoteProcessStarted(int,int)));
connect(m_runner, SIGNAL(remoteProcessFinished(QString)), connect(m_runner, SIGNAL(remoteProcessFinished(QString)),
@@ -147,6 +151,11 @@ AndroidDebugSupport::AndroidDebugSupport(AndroidRunConfiguration *runConfig,
SLOT(handleRemoteOutput(QByteArray))); SLOT(handleRemoteOutput(QByteArray)));
} }
void AndroidDebugSupport::handleRemoteServerRunning(const QByteArray &serverChannel, int pid)
{
m_runControl->engine()->notifyEngineRemoteServerRunning(serverChannel, pid);
}
void AndroidDebugSupport::handleRemoteProcessStarted(int gdbServerPort, int qmlPort) void AndroidDebugSupport::handleRemoteProcessStarted(int gdbServerPort, int qmlPort)
{ {
disconnect(m_runner, SIGNAL(remoteProcessStarted(int,int)), disconnect(m_runner, SIGNAL(remoteProcessStarted(int,int)),

View File

@@ -53,7 +53,8 @@ public:
Debugger::DebuggerRunControl *runControl); Debugger::DebuggerRunControl *runControl);
private slots: private slots:
void handleRemoteProcessStarted(int gdbServerPort = -1, int qmlPort = -1); void handleRemoteServerRunning(const QByteArray &serverChannel, int pid);
void handleRemoteProcessStarted(int gdbServerPort, int qmlPort);
void handleRemoteProcessFinished(const QString &errorMsg); void handleRemoteProcessFinished(const QString &errorMsg);
void handleRemoteOutput(const QByteArray &output); void handleRemoteOutput(const QByteArray &output);

View File

@@ -36,19 +36,27 @@
#include "androidmanager.h" #include "androidmanager.h"
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <utils/qtcassert.h>
#include <QTime> #include <QTime>
#include <QtConcurrentRun> #include <QtConcurrentRun>
#include <QTemporaryFile>
namespace Android { namespace Android {
namespace Internal { namespace Internal {
typedef QLatin1String _;
AndroidRunner::AndroidRunner(QObject *parent, AndroidRunConfiguration *runConfig, bool debuggingMode) AndroidRunner::AndroidRunner(QObject *parent, AndroidRunConfiguration *runConfig, bool debuggingMode)
: QThread(parent) : QThread(parent)
{ {
m_wasStarted = false;
m_useCppDebugger = debuggingMode && runConfig->debuggerAspect()->useCppDebugger(); m_useCppDebugger = debuggingMode && runConfig->debuggerAspect()->useCppDebugger();
m_useQmlDebugger = debuggingMode && runConfig->debuggerAspect()->useQmlDebugger(); m_useQmlDebugger = debuggingMode && runConfig->debuggerAspect()->useQmlDebugger();
m_remoteGdbChannel = runConfig->remoteChannel(); QString channel = runConfig->remoteChannel();
QTC_CHECK(channel.startsWith(QLatin1Char(':')));
m_localGdbServerPort = channel.mid(1).toUShort();
QTC_CHECK(m_localGdbServerPort);
m_qmlPort = runConfig->debuggerAspect()->qmlDebugServerPort(); m_qmlPort = runConfig->debuggerAspect()->qmlDebugServerPort();
ProjectExplorer::Target *target = runConfig->target(); ProjectExplorer::Target *target = runConfig->target();
AndroidDeployStep *ds = runConfig->deployStep(); AndroidDeployStep *ds = runConfig->deployStep();
@@ -61,204 +69,258 @@ AndroidRunner::AndroidRunner(QObject *parent, AndroidRunConfiguration *runConfig
m_packageName = m_intentName.left(m_intentName.indexOf(QLatin1Char('/'))); m_packageName = m_intentName.left(m_intentName.indexOf(QLatin1Char('/')));
m_deviceSerialNumber = ds->deviceSerialNumber(); m_deviceSerialNumber = ds->deviceSerialNumber();
m_processPID = -1; m_processPID = -1;
m_gdbserverPID = -1; m_adb = AndroidConfigurations::instance().adbToolPath().toString();
connect(&m_checkPIDTimer, SIGNAL(timeout()), SLOT(checkPID())); m_selector = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber);
QString packageDir = _("/data/data/") + m_packageName;
m_pingFile = packageDir + _("/debug-ping");
m_pongFile = _("/data/local/tmp/qt/debug-pong-") + m_packageName;
m_gdbserverSocket = packageDir + _("/debug-socket");
m_gdbserverPath = packageDir + _("/lib/gdbserver");
m_gdbserverCommand = m_gdbserverPath + _(" --multi +") + m_gdbserverSocket;
// Detect busybox, as we need to pass -w to ps to get wide output.
QProcess psProc;
psProc.start(m_adb, selector() << _("shell") << _("readlink") << _("$(which ps)"));
psProc.waitForFinished();
QByteArray which = psProc.readAll();
m_isBusyBox = which.startsWith("busybox");
connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardOutput()), SLOT(logcatReadStandardOutput())); connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardOutput()), SLOT(logcatReadStandardOutput()));
connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardError()) , SLOT(logcatReadStandardError())); connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardError()), SLOT(logcatReadStandardError()));
connect(&m_checkPIDTimer, SIGNAL(timeout()), SLOT(checkPID()));
} }
AndroidRunner::~AndroidRunner() AndroidRunner::~AndroidRunner()
{ {
stop(); //stop();
}
static int extractPidFromChunk(const QByteArray &chunk, int from)
{
int pos1 = chunk.indexOf(' ', from);
if (pos1 == -1)
return -1;
while (chunk[pos1] == ' ')
++pos1;
int pos3 = chunk.indexOf(' ', pos1);
int pid = chunk.mid(pos1, pos3 - pos1).toInt();
return pid;
}
static int extractPid(const QString &exeName, const QByteArray &psOutput)
{
const QByteArray needle = exeName.toUtf8() + '\r';
const int to = psOutput.indexOf(needle);
if (to == -1)
return -1;
const int from = psOutput.lastIndexOf('\n', to);
if (from == -1)
return -1;
return extractPidFromChunk(psOutput, from);
}
QByteArray AndroidRunner::runPs()
{
QProcess psProc;
QStringList args = m_selector;
args << _("shell") << _("ps");
if (m_isBusyBox)
args << _("-w");
psProc.start(m_adb, args);
psProc.waitForFinished();
return psProc.readAll();
} }
void AndroidRunner::checkPID() void AndroidRunner::checkPID()
{ {
QProcess psProc; if (!m_wasStarted)
QLatin1String psCmd = QLatin1String("ps");
QLatin1String psPidRx = QLatin1String("\\d+\\s+(\\d+)");
// Detect busybox, as we need to pass -w to it to get wide output.
psProc.start(AndroidConfigurations::instance().adbToolPath().toString(),
AndroidDeviceInfo::adbSelector(m_deviceSerialNumber)
<< QLatin1String("shell") << QLatin1String("readlink") << QLatin1String("$(which ps)"));
if (!psProc.waitForFinished(-1)) {
psProc.kill();
return; return;
} QByteArray psOut = runPs();
QByteArray which = psProc.readAll(); m_processPID = extractPid(m_packageName, psOut);
if (which.startsWith("busybox")) { if (m_processPID == -1)
psCmd = QLatin1String("ps -w");
psPidRx = QLatin1String("(\\d+)");
}
psProc.start(AndroidConfigurations::instance().adbToolPath().toString(),
AndroidDeviceInfo::adbSelector(m_deviceSerialNumber)
<< QLatin1String("shell") << psCmd);
if (!psProc.waitForFinished(-1)) {
psProc.kill();
return;
}
QRegExp rx(psPidRx);
qint64 pid = -1;
QList<QByteArray> procs = psProc.readAll().split('\n');
foreach (const QByteArray &proc, procs) {
if (proc.trimmed().endsWith(m_packageName.toLatin1())) {
if (rx.indexIn(QLatin1String(proc)) > -1) {
pid = rx.cap(1).toLongLong();
break;
}
}
}
if (-1 != m_processPID && pid == -1) {
m_processPID = -1;
emit remoteProcessFinished(tr("\n\n'%1' died.").arg(m_packageName)); emit remoteProcessFinished(tr("\n\n'%1' died.").arg(m_packageName));
return; }
}
m_processPID = pid;
if (!m_useCppDebugger) void AndroidRunner::forceStop()
return; {
m_gdbserverPID = -1; QProcess proc;
foreach (const QByteArray &proc, procs) { proc.start(m_adb, selector() << _("shell") << _("am") << _("force-stop"));
if (proc.trimmed().endsWith("gdbserver")) { proc.waitForFinished();
if (rx.indexIn(QLatin1String(proc)) > -1) {
m_gdbserverPID = rx.cap(1).toLongLong();
break;
}
}
}
} }
void AndroidRunner::killPID() void AndroidRunner::killPID()
{ {
checkPID(); //updates m_processPID and m_gdbserverPID const QByteArray out = runPs();
for (int tries = 0; tries < 10 && (m_processPID != -1 || m_gdbserverPID != -1); ++tries) { int from = 0;
if (m_processPID != -1) { while (1) {
adbKill(m_processPID, m_deviceSerialNumber, 2000); const int to = out.indexOf('\n', from);
adbKill(m_processPID, m_deviceSerialNumber, 2000, m_packageName); if (to == -1)
break;
QString line = QString::fromUtf8(out.data() + from, to - from - 1);
if (line.endsWith(m_packageName) || line.endsWith(m_gdbserverPath)) {
int pid = extractPidFromChunk(out, from);
adbKill(pid);
} }
from = to + 1;
if (m_gdbserverPID != -1) {
adbKill(m_gdbserverPID, m_deviceSerialNumber, 2000);
adbKill(m_gdbserverPID, m_deviceSerialNumber, 2000, m_packageName);
}
checkPID();
} }
} }
void AndroidRunner::start() void AndroidRunner::start()
{ {
QtConcurrent::run(this,&AndroidRunner::asyncStart); m_adbLogcatProcess.start(m_adb, selector() << _("logcat"));
m_wasStarted = false;
m_checkPIDTimer.start(1000); // check if the application is alive every 1 seconds
QtConcurrent::run(this, &AndroidRunner::asyncStart);
} }
void AndroidRunner::asyncStart() void AndroidRunner::asyncStart()
{ {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
m_processPID = -1; forceStop();
killPID(); // kill any process with this name killPID();
QString extraParams;
QProcess adbStarProc;
if (m_useCppDebugger) { if (m_useCppDebugger) {
QStringList arguments = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber); // Remove pong file.
arguments << QLatin1String("forward") << QString::fromLatin1("tcp%1").arg(m_remoteGdbChannel) QProcess adb;
<< QString::fromLatin1("localfilesystem:/data/data/%1/debug-socket").arg(m_packageName); adb.start(m_adb, selector() << _("shell") << _("rm") << m_pongFile);
adbStarProc.start(AndroidConfigurations::instance().adbToolPath().toString(), arguments); adb.waitForFinished();
if (!adbStarProc.waitForStarted()) { }
emit remoteProcessFinished(tr("Failed to forward C++ debugging ports. Reason: %1.").arg(adbStarProc.errorString()));
QStringList args = selector();
args << _("shell") << _("am") << _("start") << _("-n") << m_intentName;
if (m_useCppDebugger) {
QProcess adb;
adb.start(m_adb, selector() << _("forward")
<< QString::fromLatin1("tcp:%1").arg(m_localGdbServerPort)
<< _("localfilesystem:") + m_gdbserverSocket);
if (!adb.waitForStarted()) {
emit remoteProcessFinished(tr("Failed to forward C++ debugging ports. Reason: %1.").arg(adb.errorString()));
return; return;
} }
if (!adbStarProc.waitForFinished(-1)) { if (!adb.waitForFinished(-1)) {
emit remoteProcessFinished(tr("Failed to forward C++ debugging ports.")); emit remoteProcessFinished(tr("Failed to forward C++ debugging ports."));
return; return;
} }
extraParams = QLatin1String("-e native_debug true -e gdbserver_socket +debug-socket");
args << _("-e") << _("debug_ping") << _("true");
args << _("-e") << _("ping_file") << m_pingFile;
args << _("-e") << _("pong_file") << m_pongFile;
args << _("-e") << _("gdbserver_command") << m_gdbserverCommand;
args << _("-e") << _("gdbserver_socket") << m_gdbserverSocket;
} }
if (m_useQmlDebugger) { if (m_useQmlDebugger) {
QStringList arguments = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber); // currently forward to same port on device and host
QString port = QString::fromLatin1("tcp:%1").arg(m_qmlPort); QString port = QString::fromLatin1("tcp:%1").arg(m_qmlPort);
arguments << QLatin1String("forward") << port << port; // currently forward to same port on device and host QProcess adb;
adbStarProc.start(AndroidConfigurations::instance().adbToolPath().toString(), arguments); adb.start(m_adb, selector() << _("forward") << port << port);
if (!adbStarProc.waitForStarted()) { if (!adb.waitForStarted()) {
emit remoteProcessFinished(tr("Failed to forward QML debugging ports. Reason: %1.").arg(adbStarProc.errorString())); emit remoteProcessFinished(tr("Failed to forward QML debugging ports. Reason: %1.").arg(adb.errorString()));
return; return;
} }
if (!adbStarProc.waitForFinished(-1)) { if (!adb.waitForFinished()) {
emit remoteProcessFinished(tr("Failed to forward QML debugging ports.")); emit remoteProcessFinished(tr("Failed to forward QML debugging ports."));
return; return;
} }
extraParams+=QString::fromLatin1(" -e qml_debug true -e qmljsdebugger port:%1") args << _("-e") << _("qml_debug") << _("true");
.arg(m_qmlPort); args << _("-e") << _("qmljsdebugger") << QString::fromLatin1("port:%1").arg(m_qmlPort);
} }
if (m_useLocalQtLibs) { if (m_useLocalQtLibs) {
extraParams += QLatin1String(" -e use_local_qt_libs true"); args << _("-e") << _("use_local_qt_libs") << _("true");
extraParams += QLatin1String(" -e libs_prefix /data/local/tmp/qt/"); args << _("-e") << _("libs_prefix") << _("/data/local/tmp/qt/");
extraParams += QLatin1String(" -e load_local_libs ") + m_localLibs; args << _("-e") << _("load_local_libs") << m_localLibs;
extraParams += QLatin1String(" -e load_local_jars ") + m_localJars; args << _("-e") << _("load_local_jars") << m_localJars;
if (!m_localJarsInitClasses.isEmpty()) if (!m_localJarsInitClasses.isEmpty())
extraParams += QLatin1String(" -e static_init_classes ") + m_localJarsInitClasses; args << _("-e") << _("static_init_classes") << m_localJarsInitClasses;
} }
extraParams = extraParams.trimmed(); QProcess adb;
QStringList arguments = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber); adb.start(m_adb, args);
arguments << QLatin1String("shell") << QLatin1String("am") if (!adb.waitForStarted()) {
<< QLatin1String("start") << QLatin1String("-n") << m_intentName; emit remoteProcessFinished(tr("Failed to start the activity. Reason: %1.").arg(adb.errorString()));
if (extraParams.length())
arguments << extraParams.split(QLatin1Char(' '));
adbStarProc.start(AndroidConfigurations::instance().adbToolPath().toString(), arguments);
if (!adbStarProc.waitForStarted()) {
emit remoteProcessFinished(tr("Failed to start the activity. Reason: %1.").arg(adbStarProc.errorString()));
return; return;
} }
if (!adbStarProc.waitForFinished(-1)) { if (!adb.waitForFinished(-1)) {
adbStarProc.terminate(); adb.terminate();
emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName)); emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName));
return; return;
} }
QTime startTime = QTime::currentTime();
while (m_processPID == -1 && startTime.secsTo(QTime::currentTime()) < 10) { // wait up to 10 seconds for application to start if (m_useCppDebugger || m_useQmlDebugger) {
checkPID();
// Handling ping.
for (int i = 0; ; ++i) {
QTemporaryFile tmp(_("pingpong"));
tmp.open();
tmp.close();
QProcess process;
process.start(m_adb, selector() << _("pull") << m_pingFile << tmp.fileName());
process.waitForFinished();
QFile res(tmp.fileName());
const bool doBreak = res.size();
res.remove();
if (doBreak)
break;
if (i == 20) {
emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName));
return;
}
qDebug() << "WAITING FOR " << tmp.fileName();
QThread::msleep(500);
}
} }
QByteArray psOut = runPs();
m_processPID = extractPid(m_packageName, psOut);
if (m_processPID == -1) { if (m_processPID == -1) {
emit remoteProcessFinished(tr("Cannot find %1 process.").arg(m_packageName)); emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName));
return; return;
} }
if (m_useCppDebugger) { m_wasStarted = true;
startTime = QTime::currentTime(); if (m_useCppDebugger || m_useQmlDebugger) {
while (m_gdbserverPID == -1 && startTime.secsTo(QTime::currentTime()) < 25) { // wait up to 25 seconds to connect // This will be funneled to the engine to actually start and attach
checkPID(); // gdb. Afterwards this ends up in handleGdbRunning() below.
} QByteArray serverChannel = ':' + QByteArray::number(m_localGdbServerPort);
msleep(200); // give gdbserver more time to start emit remoteServerRunning(serverChannel, m_processPID);
} else {
// Start without debugging.
emit remoteProcessStarted(-1, -1);
} }
QMetaObject::invokeMethod(this, "startLogcat", Qt::QueuedConnection);
} }
void AndroidRunner::startLogcat() void AndroidRunner::handleGdbRunning()
{ {
m_checkPIDTimer.start(1000); // check if the application is alive every 1 seconds QTemporaryFile tmp(_("pingpong"));
m_adbLogcatProcess.start(AndroidConfigurations::instance().adbToolPath().toString(), tmp.open();
AndroidDeviceInfo::adbSelector(m_deviceSerialNumber)
<< QLatin1String("logcat")); QProcess process;
emit remoteProcessStarted(5039); process.start(m_adb, selector() << _("push") << tmp.fileName() << m_pongFile);
process.waitForFinished();
QTC_CHECK(m_processPID != -1);
emit remoteProcessStarted(m_localGdbServerPort, -1);
} }
void AndroidRunner::stop() void AndroidRunner::stop()
{ {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
m_checkPIDTimer.stop(); m_checkPIDTimer.stop();
if (m_processPID == -1) { if (m_processPID != -1) {
m_adbLogcatProcess.kill(); killPID();
return; // don't emit another signal emit remoteProcessFinished(tr("\n\n'%1' terminated.").arg(m_packageName));
} }
killPID(); //QObject::disconnect(&m_adbLogcatProcess, 0, this, 0);
m_adbLogcatProcess.kill(); m_adbLogcatProcess.kill();
m_adbLogcatProcess.waitForFinished(-1); m_adbLogcatProcess.waitForFinished();
} }
void AndroidRunner::logcatReadStandardError() void AndroidRunner::logcatReadStandardError()
@@ -288,20 +350,21 @@ void AndroidRunner::logcatReadStandardOutput()
m_logcat = line; m_logcat = line;
} }
void AndroidRunner::adbKill(qint64 pid, const QString &device, int timeout, const QString &runAsPackageName) void AndroidRunner::adbKill(qint64 pid)
{ {
QProcess process; {
QStringList arguments = AndroidDeviceInfo::adbSelector(device); QProcess process;
process.start(m_adb, selector() << _("shell")
arguments << QLatin1String("shell"); << _("kill") << QLatin1String("-9") << QString::number(pid));
if (runAsPackageName.size()) process.waitForFinished();
arguments << QLatin1String("run-as") << runAsPackageName; }
arguments << QLatin1String("kill") << QLatin1String("-9"); {
arguments << QString::number(pid); QProcess process;
process.start(m_adb, selector() << _("shell")
process.start(AndroidConfigurations::instance().adbToolPath().toString(), arguments); << _("run-as") << m_packageName
if (!process.waitForFinished(timeout)) << _("kill") << QLatin1String("-9") << QString::number(pid));
process.kill(); process.waitForFinished();
}
} }
QString AndroidRunner::displayName() const QString AndroidRunner::displayName() const

View File

@@ -56,9 +56,11 @@ public:
public slots: public slots:
void start(); void start();
void stop(); void stop();
void handleGdbRunning();
signals: signals:
void remoteProcessStarted(int gdbServerPort = -1, int qmlPort = -1); void remoteServerRunning(const QByteArray &serverChannel, int pid);
void remoteProcessStarted(int gdbServerPort, int qmlPort);
void remoteProcessFinished(const QString &errString = QString()); void remoteProcessFinished(const QString &errString = QString());
void remoteOutput(const QByteArray &output); void remoteOutput(const QByteArray &output);
@@ -69,29 +71,41 @@ private slots:
void checkPID(); void checkPID();
void logcatReadStandardError(); void logcatReadStandardError();
void logcatReadStandardOutput(); void logcatReadStandardOutput();
void startLogcat();
void asyncStart(); void asyncStart();
private: private:
void adbKill(qint64 pid, const QString &device, int timeout = 2000, const QString &runAsPackageName = QString()); void adbKill(qint64 pid);
QStringList selector() const { return m_selector; }
void forceStop();
QByteArray runPs();
void findPs();
private: private:
QProcess m_adbLogcatProcess; QProcess m_adbLogcatProcess;
QTimer m_checkPIDTimer;
bool m_wasStarted;
QByteArray m_logcat; QByteArray m_logcat;
QString m_intentName; QString m_intentName;
QString m_packageName; QString m_packageName;
QString m_deviceSerialNumber; QString m_deviceSerialNumber;
qint64 m_processPID; qint64 m_processPID;
qint64 m_gdbserverPID;
QTimer m_checkPIDTimer;
bool m_useCppDebugger; bool m_useCppDebugger;
bool m_useQmlDebugger; bool m_useQmlDebugger;
QString m_remoteGdbChannel; ushort m_localGdbServerPort; // Local end of forwarded debug socket.
uint m_qmlPort; uint m_qmlPort;
bool m_useLocalQtLibs; bool m_useLocalQtLibs;
QString m_pingFile;
QString m_pongFile;
QString m_gdbserverPath;
QString m_gdbserverCommand;
QString m_gdbserverSocket;
QString m_localLibs; QString m_localLibs;
QString m_localJars; QString m_localJars;
QString m_localJarsInitClasses; QString m_localJarsInitClasses;
QString m_adb;
bool m_isBusyBox;
QStringList m_selector;
QMutex m_mutex; QMutex m_mutex;
}; };

View File

@@ -879,6 +879,11 @@ void DebuggerEngine::notifyEngineRequestRemoteSetup()
emit requestRemoteSetup(); emit requestRemoteSetup();
} }
void DebuggerEngine::notifyEngineRemoteServerRunning(const QByteArray &, int /*pid*/)
{
showMessage(_("NOTE: REMOTE SERVER RUNNING IN MULTIMODE"));
}
void DebuggerEngine::notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort) void DebuggerEngine::notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort)
{ {
showMessage(_("NOTE: REMOTE SETUP DONE: GDB SERVER PORT: %1 QML PORT %2") showMessage(_("NOTE: REMOTE SETUP DONE: GDB SERVER PORT: %1 QML PORT %2")

View File

@@ -299,6 +299,7 @@ protected:
virtual void notifyEngineRequestRemoteSetup(); virtual void notifyEngineRequestRemoteSetup();
public: public:
virtual void notifyEngineRemoteServerRunning(const QByteArray &, int pid);
virtual void notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort); virtual void notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort);
virtual void notifyEngineRemoteSetupFailed(const QString &message); virtual void notifyEngineRemoteSetupFailed(const QString &message);

View File

@@ -1267,6 +1267,7 @@ void GdbEngine::handleResultRecord(GdbResponse *response)
bool GdbEngine::acceptsDebuggerCommands() const bool GdbEngine::acceptsDebuggerCommands() const
{ {
return true;
return state() == InferiorStopOk return state() == InferiorStopOk
|| state() == InferiorUnrunnable; || state() == InferiorUnrunnable;
} }
@@ -3740,7 +3741,7 @@ void GdbEngine::handleThreadInfo(const GdbResponse &response)
selectThread(other); selectThread(other);
} }
updateViews(); // Adjust Threads combobox. updateViews(); // Adjust Threads combobox.
if (m_hasInferiorThreadList && debuggerCore()->boolSetting(ShowThreadNames)) { if (false && m_hasInferiorThreadList && debuggerCore()->boolSetting(ShowThreadNames)) {
postCommand("threadnames " + postCommand("threadnames " +
debuggerCore()->action(MaximalStackDepth)->value().toByteArray(), debuggerCore()->action(MaximalStackDepth)->value().toByteArray(),
Discardable, CB(handleThreadNames)); Discardable, CB(handleThreadNames));

View File

@@ -60,6 +60,8 @@ namespace Internal {
GdbRemoteServerEngine::GdbRemoteServerEngine(const DebuggerStartParameters &startParameters) GdbRemoteServerEngine::GdbRemoteServerEngine(const DebuggerStartParameters &startParameters)
: GdbEngine(startParameters) : GdbEngine(startParameters)
{ {
m_isMulti = false;
m_targetPid = -1;
connect(&m_uploadProc, SIGNAL(error(QProcess::ProcessError)), connect(&m_uploadProc, SIGNAL(error(QProcess::ProcessError)),
SLOT(uploadProcError(QProcess::ProcessError))); SLOT(uploadProcError(QProcess::ProcessError)));
connect(&m_uploadProc, SIGNAL(readyReadStandardOutput()), connect(&m_uploadProc, SIGNAL(readyReadStandardOutput()),
@@ -261,13 +263,8 @@ void GdbRemoteServerEngine::handleFileExecAndSymbols(const GdbResponse &response
void GdbRemoteServerEngine::callTargetRemote() void GdbRemoteServerEngine::callTargetRemote()
{ {
//m_breakHandler->clearBreakMarkers(); QByteArray rawChannel = startParameters().remoteChannel.toLatin1();
QByteArray channel = rawChannel;
// "target remote" does three things:
// (1) connects to the gdb server
// (2) starts the remote application
// (3) stops the remote application (early, e.g. in the dynamic linker)
QByteArray channel = startParameters().remoteChannel.toLatin1();
// Don't touch channels with explicitly set protocols. // Don't touch channels with explicitly set protocols.
if (!channel.startsWith("tcp:") && !channel.startsWith("udp:") if (!channel.startsWith("tcp:") && !channel.startsWith("udp:")
@@ -283,14 +280,16 @@ void GdbRemoteServerEngine::callTargetRemote()
if (m_isQnxGdb) if (m_isQnxGdb)
postCommand("target qnx " + channel, CB(handleTargetQnx)); postCommand("target qnx " + channel, CB(handleTargetQnx));
else if (m_isMulti)
postCommand("target extended-remote " + m_serverChannel, CB(handleTargetExtendedRemote));
else else
postCommand("target remote " + channel, CB(handleTargetRemote)); postCommand("target remote " + channel, CB(handleTargetRemote), 10);
} }
void GdbRemoteServerEngine::handleTargetRemote(const GdbResponse &record) void GdbRemoteServerEngine::handleTargetRemote(const GdbResponse &response)
{ {
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
if (record.resultClass == GdbResultDone) { if (response.resultClass == GdbResultDone) {
// gdb server will stop the remote application itself. // gdb server will stop the remote application itself.
showMessage(_("INFERIOR STARTED")); showMessage(_("INFERIOR STARTED"));
showMessage(msgAttachedToStoppedInferior(), StatusBar); showMessage(msgAttachedToStoppedInferior(), StatusBar);
@@ -303,11 +302,50 @@ void GdbRemoteServerEngine::handleTargetRemote(const GdbResponse &record)
} else { } else {
// 16^error,msg="hd:5555: Connection timed out." // 16^error,msg="hd:5555: Connection timed out."
QString msg = msgConnectRemoteServerFailed( QString msg = msgConnectRemoteServerFailed(
QString::fromLocal8Bit(record.data.findChild("msg").data())); QString::fromLocal8Bit(response.data.findChild("msg").data()));
notifyInferiorSetupFailed(msg); notifyInferiorSetupFailed(msg);
} }
} }
void GdbRemoteServerEngine::handleTargetExtendedRemote(const GdbResponse &response)
{
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
if (response.resultClass == GdbResultDone) {
// gdb server will stop the remote application itself.
showMessage(_("ATTACHED TO GDB SERVER STARTED"));
showMessage(msgAttachedToStoppedInferior(), StatusBar);
QString postAttachCommands = debuggerCore()->stringSetting(GdbPostAttachCommands);
if (!postAttachCommands.isEmpty()) {
foreach (const QString &cmd, postAttachCommands.split(QLatin1Char('\n')))
postCommand(cmd.toLatin1());
}
postCommand("attach " + QByteArray::number(m_targetPid), CB(handleTargetExtendedAttach));
} else {
QString msg = msgConnectRemoteServerFailed(
QString::fromLocal8Bit(response.data.findChild("msg").data()));
notifyInferiorSetupFailed(msg);
}
}
void GdbRemoteServerEngine::handleTargetExtendedAttach(const GdbResponse &response)
{
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
if (response.resultClass == GdbResultDone) {
// gdb server will stop the remote application itself.
handleInferiorPrepared();
} else {
QString msg = msgConnectRemoteServerFailed(
QString::fromLocal8Bit(response.data.findChild("msg").data()));
notifyInferiorSetupFailed(msg);
}
}
void GdbRemoteServerEngine::notifyInferiorSetupOk()
{
emit aboutToNotifyInferiorSetupOk();
GdbEngine::notifyInferiorSetupOk();
}
void GdbRemoteServerEngine::handleTargetQnx(const GdbResponse &response) void GdbRemoteServerEngine::handleTargetQnx(const GdbResponse &response)
{ {
QTC_ASSERT(m_isQnxGdb, qDebug() << m_isQnxGdb); QTC_ASSERT(m_isQnxGdb, qDebug() << m_isQnxGdb);
@@ -417,22 +455,36 @@ void GdbRemoteServerEngine::shutdownEngine()
notifyAdapterShutdownOk(); notifyAdapterShutdownOk();
} }
void GdbRemoteServerEngine::notifyEngineRemoteServerRunning
(const QByteArray &serverChannel, int inferiorPid)
{
showMessage(_("NOTE: REMOTE SERVER RUNNING IN MULTIMODE"));
m_isMulti = true;
m_targetPid = inferiorPid;
m_serverChannel = serverChannel;
startGdb();
}
void GdbRemoteServerEngine::notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort) void GdbRemoteServerEngine::notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort)
{ {
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
DebuggerEngine::notifyEngineRemoteSetupDone(gdbServerPort, qmlPort); DebuggerEngine::notifyEngineRemoteSetupDone(gdbServerPort, qmlPort);
if (qmlPort != -1) if (m_isMulti) {
startParameters().qmlServerPort = qmlPort; // Has been done in notifyEngineRemoteServerRunning
if (gdbServerPort != -1) { } else {
QString &rc = startParameters().remoteChannel; if (qmlPort != -1)
const int sepIndex = rc.lastIndexOf(QLatin1Char(':')); startParameters().qmlServerPort = qmlPort;
if (sepIndex != -1) { if (gdbServerPort != -1) {
rc.replace(sepIndex + 1, rc.count() - sepIndex - 1, QString &rc = startParameters().remoteChannel;
QString::number(gdbServerPort)); const int sepIndex = rc.lastIndexOf(QLatin1Char(':'));
if (sepIndex != -1) {
rc.replace(sepIndex + 1, rc.count() - sepIndex - 1,
QString::number(gdbServerPort));
}
} }
startGdb();
} }
startGdb();
} }
void GdbRemoteServerEngine::notifyEngineRemoteSetupFailed(const QString &reason) void GdbRemoteServerEngine::notifyEngineRemoteSetupFailed(const QString &reason)

View File

@@ -70,20 +70,25 @@ signals:
* a server start script should be used, but none is given. * a server start script should be used, but none is given.
*/ */
void requestSetup(); void requestSetup();
void aboutToNotifyInferiorSetupOk();
private: private:
Q_SLOT void readUploadStandardOutput(); Q_SLOT void readUploadStandardOutput();
Q_SLOT void readUploadStandardError(); Q_SLOT void readUploadStandardError();
Q_SLOT void uploadProcError(QProcess::ProcessError error); Q_SLOT void uploadProcError(QProcess::ProcessError error);
Q_SLOT void uploadProcFinished(); Q_SLOT void uploadProcFinished();
Q_SLOT void callTargetRemote();
virtual void notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort); void notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort);
virtual void notifyEngineRemoteSetupFailed(const QString &reason); void notifyEngineRemoteSetupFailed(const QString &reason);
void notifyEngineRemoteServerRunning(const QByteArray &serverChannel, int inferiorPid);
void notifyInferiorSetupOk();
void handleSetTargetAsync(const GdbResponse &response); void handleSetTargetAsync(const GdbResponse &response);
void handleFileExecAndSymbols(const GdbResponse &response); void handleFileExecAndSymbols(const GdbResponse &response);
void callTargetRemote();
void handleTargetRemote(const GdbResponse &response); void handleTargetRemote(const GdbResponse &response);
void handleTargetExtendedRemote(const GdbResponse &response);
void handleTargetExtendedAttach(const GdbResponse &response);
void handleTargetQnx(const GdbResponse &response); void handleTargetQnx(const GdbResponse &response);
void handleAttach(const GdbResponse &response); void handleAttach(const GdbResponse &response);
void handleInterruptInferior(const GdbResponse &response); void handleInterruptInferior(const GdbResponse &response);
@@ -91,6 +96,9 @@ private:
QProcess m_uploadProc; QProcess m_uploadProc;
LocalGdbProcess m_gdbProc; LocalGdbProcess m_gdbProc;
bool m_isMulti;
int m_targetPid;
QByteArray m_serverChannel;
}; };
} // namespace Internal } // namespace Internal