From 066efcd6a601e75fa0a1b748ad6d341712507dcc Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 27 Feb 2013 15:12:36 +0100 Subject: [PATCH] Debugging on Android This implements the host side of https://codereview.qt-project.org/#change,50290 Change-Id: I13c7df29534a2a85202c2b295b139896443b0120 Reviewed-by: Eskil Abrahamsen Blomfeldt Reviewed-by: Daniel Teske Reviewed-by: BogDan Vatra Reviewed-by: hjk --- src/plugins/android/androiddebugsupport.cpp | 9 + src/plugins/android/androiddebugsupport.h | 3 +- src/plugins/android/androidrunner.cpp | 349 +++++++++++------- src/plugins/android/androidrunner.h | 26 +- src/plugins/debugger/debuggerengine.cpp | 5 + src/plugins/debugger/debuggerengine.h | 1 + src/plugins/debugger/gdb/gdbengine.cpp | 3 +- .../debugger/gdb/remotegdbserveradapter.cpp | 92 ++++- .../debugger/gdb/remotegdbserveradapter.h | 14 +- 9 files changed, 328 insertions(+), 174 deletions(-) diff --git a/src/plugins/android/androiddebugsupport.cpp b/src/plugins/android/androiddebugsupport.cpp index c5eb00bdf6f..92fab2c66a5 100644 --- a/src/plugins/android/androiddebugsupport.cpp +++ b/src/plugins/android/androiddebugsupport.cpp @@ -135,7 +135,11 @@ AndroidDebugSupport::AndroidDebugSupport(AndroidRunConfiguration *runConfig, m_runner, SLOT(start())); connect(m_runControl, SIGNAL(finished()), 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)), SLOT(handleRemoteProcessStarted(int,int))); connect(m_runner, SIGNAL(remoteProcessFinished(QString)), @@ -147,6 +151,11 @@ AndroidDebugSupport::AndroidDebugSupport(AndroidRunConfiguration *runConfig, SLOT(handleRemoteOutput(QByteArray))); } +void AndroidDebugSupport::handleRemoteServerRunning(const QByteArray &serverChannel, int pid) +{ + m_runControl->engine()->notifyEngineRemoteServerRunning(serverChannel, pid); +} + void AndroidDebugSupport::handleRemoteProcessStarted(int gdbServerPort, int qmlPort) { disconnect(m_runner, SIGNAL(remoteProcessStarted(int,int)), diff --git a/src/plugins/android/androiddebugsupport.h b/src/plugins/android/androiddebugsupport.h index 788e51b95d4..90a6324fd5e 100644 --- a/src/plugins/android/androiddebugsupport.h +++ b/src/plugins/android/androiddebugsupport.h @@ -53,7 +53,8 @@ public: Debugger::DebuggerRunControl *runControl); 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 handleRemoteOutput(const QByteArray &output); diff --git a/src/plugins/android/androidrunner.cpp b/src/plugins/android/androidrunner.cpp index 7b7d9633437..f820a738355 100644 --- a/src/plugins/android/androidrunner.cpp +++ b/src/plugins/android/androidrunner.cpp @@ -36,19 +36,27 @@ #include "androidmanager.h" #include +#include #include #include +#include namespace Android { namespace Internal { +typedef QLatin1String _; + AndroidRunner::AndroidRunner(QObject *parent, AndroidRunConfiguration *runConfig, bool debuggingMode) : QThread(parent) { + m_wasStarted = false; m_useCppDebugger = debuggingMode && runConfig->debuggerAspect()->useCppDebugger(); 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(); ProjectExplorer::Target *target = runConfig->target(); AndroidDeployStep *ds = runConfig->deployStep(); @@ -61,204 +69,258 @@ AndroidRunner::AndroidRunner(QObject *parent, AndroidRunConfiguration *runConfig m_packageName = m_intentName.left(m_intentName.indexOf(QLatin1Char('/'))); m_deviceSerialNumber = ds->deviceSerialNumber(); m_processPID = -1; - m_gdbserverPID = -1; - connect(&m_checkPIDTimer, SIGNAL(timeout()), SLOT(checkPID())); + m_adb = AndroidConfigurations::instance().adbToolPath().toString(); + 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(readyReadStandardError()) , SLOT(logcatReadStandardError())); + connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardError()), SLOT(logcatReadStandardError())); + connect(&m_checkPIDTimer, SIGNAL(timeout()), SLOT(checkPID())); } 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() { - QProcess psProc; - 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(); + if (!m_wasStarted) return; - } - QByteArray which = psProc.readAll(); - if (which.startsWith("busybox")) { - 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 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; + QByteArray psOut = runPs(); + m_processPID = extractPid(m_packageName, psOut); + if (m_processPID == -1) emit remoteProcessFinished(tr("\n\n'%1' died.").arg(m_packageName)); - return; - } - m_processPID = pid; +} - if (!m_useCppDebugger) - return; - m_gdbserverPID = -1; - foreach (const QByteArray &proc, procs) { - if (proc.trimmed().endsWith("gdbserver")) { - if (rx.indexIn(QLatin1String(proc)) > -1) { - m_gdbserverPID = rx.cap(1).toLongLong(); - break; - } - } - } +void AndroidRunner::forceStop() +{ + QProcess proc; + proc.start(m_adb, selector() << _("shell") << _("am") << _("force-stop")); + proc.waitForFinished(); } void AndroidRunner::killPID() { - checkPID(); //updates m_processPID and m_gdbserverPID - for (int tries = 0; tries < 10 && (m_processPID != -1 || m_gdbserverPID != -1); ++tries) { - if (m_processPID != -1) { - adbKill(m_processPID, m_deviceSerialNumber, 2000); - adbKill(m_processPID, m_deviceSerialNumber, 2000, m_packageName); + const QByteArray out = runPs(); + int from = 0; + while (1) { + const int to = out.indexOf('\n', from); + 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); } - - if (m_gdbserverPID != -1) { - adbKill(m_gdbserverPID, m_deviceSerialNumber, 2000); - adbKill(m_gdbserverPID, m_deviceSerialNumber, 2000, m_packageName); - } - checkPID(); + from = to + 1; } } 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() { QMutexLocker locker(&m_mutex); - m_processPID = -1; - killPID(); // kill any process with this name - QString extraParams; - QProcess adbStarProc; + forceStop(); + killPID(); + if (m_useCppDebugger) { - QStringList arguments = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber); - arguments << QLatin1String("forward") << QString::fromLatin1("tcp%1").arg(m_remoteGdbChannel) - << QString::fromLatin1("localfilesystem:/data/data/%1/debug-socket").arg(m_packageName); - adbStarProc.start(AndroidConfigurations::instance().adbToolPath().toString(), arguments); - if (!adbStarProc.waitForStarted()) { - emit remoteProcessFinished(tr("Failed to forward C++ debugging ports. Reason: %1.").arg(adbStarProc.errorString())); + // Remove pong file. + QProcess adb; + adb.start(m_adb, selector() << _("shell") << _("rm") << m_pongFile); + adb.waitForFinished(); + } + + 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; } - if (!adbStarProc.waitForFinished(-1)) { + if (!adb.waitForFinished(-1)) { emit remoteProcessFinished(tr("Failed to forward C++ debugging ports.")); 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) { - QStringList arguments = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber); + // currently forward to same port on device and host QString port = QString::fromLatin1("tcp:%1").arg(m_qmlPort); - arguments << QLatin1String("forward") << port << port; // currently forward to same port on device and host - adbStarProc.start(AndroidConfigurations::instance().adbToolPath().toString(), arguments); - if (!adbStarProc.waitForStarted()) { - emit remoteProcessFinished(tr("Failed to forward QML debugging ports. Reason: %1.").arg(adbStarProc.errorString())); + QProcess adb; + adb.start(m_adb, selector() << _("forward") << port << port); + if (!adb.waitForStarted()) { + emit remoteProcessFinished(tr("Failed to forward QML debugging ports. Reason: %1.").arg(adb.errorString())); return; } - if (!adbStarProc.waitForFinished(-1)) { + if (!adb.waitForFinished()) { emit remoteProcessFinished(tr("Failed to forward QML debugging ports.")); return; } - extraParams+=QString::fromLatin1(" -e qml_debug true -e qmljsdebugger port:%1") - .arg(m_qmlPort); + args << _("-e") << _("qml_debug") << _("true"); + args << _("-e") << _("qmljsdebugger") << QString::fromLatin1("port:%1").arg(m_qmlPort); } if (m_useLocalQtLibs) { - extraParams += QLatin1String(" -e use_local_qt_libs true"); - extraParams += QLatin1String(" -e libs_prefix /data/local/tmp/qt/"); - extraParams += QLatin1String(" -e load_local_libs ") + m_localLibs; - extraParams += QLatin1String(" -e load_local_jars ") + m_localJars; + args << _("-e") << _("use_local_qt_libs") << _("true"); + args << _("-e") << _("libs_prefix") << _("/data/local/tmp/qt/"); + args << _("-e") << _("load_local_libs") << m_localLibs; + args << _("-e") << _("load_local_jars") << m_localJars; if (!m_localJarsInitClasses.isEmpty()) - extraParams += QLatin1String(" -e static_init_classes ") + m_localJarsInitClasses; + args << _("-e") << _("static_init_classes") << m_localJarsInitClasses; } - extraParams = extraParams.trimmed(); - QStringList arguments = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber); - arguments << QLatin1String("shell") << QLatin1String("am") - << QLatin1String("start") << QLatin1String("-n") << m_intentName; - - 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())); + QProcess adb; + adb.start(m_adb, args); + if (!adb.waitForStarted()) { + emit remoteProcessFinished(tr("Failed to start the activity. Reason: %1.").arg(adb.errorString())); return; } - if (!adbStarProc.waitForFinished(-1)) { - adbStarProc.terminate(); + if (!adb.waitForFinished(-1)) { + adb.terminate(); emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName)); return; } - QTime startTime = QTime::currentTime(); - while (m_processPID == -1 && startTime.secsTo(QTime::currentTime()) < 10) { // wait up to 10 seconds for application to start - checkPID(); + + if (m_useCppDebugger || m_useQmlDebugger) { + + // 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) { - emit remoteProcessFinished(tr("Cannot find %1 process.").arg(m_packageName)); + emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName)); return; } - if (m_useCppDebugger) { - startTime = QTime::currentTime(); - while (m_gdbserverPID == -1 && startTime.secsTo(QTime::currentTime()) < 25) { // wait up to 25 seconds to connect - checkPID(); - } - msleep(200); // give gdbserver more time to start + m_wasStarted = true; + if (m_useCppDebugger || m_useQmlDebugger) { + // This will be funneled to the engine to actually start and attach + // gdb. Afterwards this ends up in handleGdbRunning() below. + QByteArray serverChannel = ':' + QByteArray::number(m_localGdbServerPort); + 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 - m_adbLogcatProcess.start(AndroidConfigurations::instance().adbToolPath().toString(), - AndroidDeviceInfo::adbSelector(m_deviceSerialNumber) - << QLatin1String("logcat")); - emit remoteProcessStarted(5039); + QTemporaryFile tmp(_("pingpong")); + tmp.open(); + + QProcess process; + 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() { QMutexLocker locker(&m_mutex); m_checkPIDTimer.stop(); - if (m_processPID == -1) { - m_adbLogcatProcess.kill(); - return; // don't emit another signal + if (m_processPID != -1) { + killPID(); + emit remoteProcessFinished(tr("\n\n'%1' terminated.").arg(m_packageName)); } - killPID(); + //QObject::disconnect(&m_adbLogcatProcess, 0, this, 0); m_adbLogcatProcess.kill(); - m_adbLogcatProcess.waitForFinished(-1); + m_adbLogcatProcess.waitForFinished(); } void AndroidRunner::logcatReadStandardError() @@ -288,20 +350,21 @@ void AndroidRunner::logcatReadStandardOutput() 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); - - arguments << QLatin1String("shell"); - if (runAsPackageName.size()) - arguments << QLatin1String("run-as") << runAsPackageName; - arguments << QLatin1String("kill") << QLatin1String("-9"); - arguments << QString::number(pid); - - process.start(AndroidConfigurations::instance().adbToolPath().toString(), arguments); - if (!process.waitForFinished(timeout)) - process.kill(); + { + QProcess process; + process.start(m_adb, selector() << _("shell") + << _("kill") << QLatin1String("-9") << QString::number(pid)); + process.waitForFinished(); + } + { + QProcess process; + process.start(m_adb, selector() << _("shell") + << _("run-as") << m_packageName + << _("kill") << QLatin1String("-9") << QString::number(pid)); + process.waitForFinished(); + } } QString AndroidRunner::displayName() const diff --git a/src/plugins/android/androidrunner.h b/src/plugins/android/androidrunner.h index 9bb654dd8b8..24e4f8ded74 100644 --- a/src/plugins/android/androidrunner.h +++ b/src/plugins/android/androidrunner.h @@ -56,9 +56,11 @@ public: public slots: void start(); void stop(); + void handleGdbRunning(); 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 remoteOutput(const QByteArray &output); @@ -69,29 +71,41 @@ private slots: void checkPID(); void logcatReadStandardError(); void logcatReadStandardOutput(); - void startLogcat(); void asyncStart(); 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: QProcess m_adbLogcatProcess; + QTimer m_checkPIDTimer; + bool m_wasStarted; + QByteArray m_logcat; QString m_intentName; QString m_packageName; QString m_deviceSerialNumber; qint64 m_processPID; - qint64 m_gdbserverPID; - QTimer m_checkPIDTimer; bool m_useCppDebugger; bool m_useQmlDebugger; - QString m_remoteGdbChannel; + ushort m_localGdbServerPort; // Local end of forwarded debug socket. uint m_qmlPort; bool m_useLocalQtLibs; + QString m_pingFile; + QString m_pongFile; + QString m_gdbserverPath; + QString m_gdbserverCommand; + QString m_gdbserverSocket; QString m_localLibs; QString m_localJars; QString m_localJarsInitClasses; + QString m_adb; + bool m_isBusyBox; + QStringList m_selector; QMutex m_mutex; }; diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index e962e949410..d5dea2905ec 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -879,6 +879,11 @@ void DebuggerEngine::notifyEngineRequestRemoteSetup() emit requestRemoteSetup(); } +void DebuggerEngine::notifyEngineRemoteServerRunning(const QByteArray &, int /*pid*/) +{ + showMessage(_("NOTE: REMOTE SERVER RUNNING IN MULTIMODE")); +} + void DebuggerEngine::notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort) { showMessage(_("NOTE: REMOTE SETUP DONE: GDB SERVER PORT: %1 QML PORT %2") diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index 64ec1ef9e6a..85144158368 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -299,6 +299,7 @@ protected: virtual void notifyEngineRequestRemoteSetup(); public: + virtual void notifyEngineRemoteServerRunning(const QByteArray &, int pid); virtual void notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort); virtual void notifyEngineRemoteSetupFailed(const QString &message); diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 9f561fc09e3..72311188975 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -1267,6 +1267,7 @@ void GdbEngine::handleResultRecord(GdbResponse *response) bool GdbEngine::acceptsDebuggerCommands() const { + return true; return state() == InferiorStopOk || state() == InferiorUnrunnable; } @@ -3740,7 +3741,7 @@ void GdbEngine::handleThreadInfo(const GdbResponse &response) selectThread(other); } updateViews(); // Adjust Threads combobox. - if (m_hasInferiorThreadList && debuggerCore()->boolSetting(ShowThreadNames)) { + if (false && m_hasInferiorThreadList && debuggerCore()->boolSetting(ShowThreadNames)) { postCommand("threadnames " + debuggerCore()->action(MaximalStackDepth)->value().toByteArray(), Discardable, CB(handleThreadNames)); diff --git a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp index a3780c8d207..70285fea1b6 100644 --- a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp +++ b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp @@ -60,6 +60,8 @@ namespace Internal { GdbRemoteServerEngine::GdbRemoteServerEngine(const DebuggerStartParameters &startParameters) : GdbEngine(startParameters) { + m_isMulti = false; + m_targetPid = -1; connect(&m_uploadProc, SIGNAL(error(QProcess::ProcessError)), SLOT(uploadProcError(QProcess::ProcessError))); connect(&m_uploadProc, SIGNAL(readyReadStandardOutput()), @@ -261,13 +263,8 @@ void GdbRemoteServerEngine::handleFileExecAndSymbols(const GdbResponse &response void GdbRemoteServerEngine::callTargetRemote() { - //m_breakHandler->clearBreakMarkers(); - - // "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(); + QByteArray rawChannel = startParameters().remoteChannel.toLatin1(); + QByteArray channel = rawChannel; // Don't touch channels with explicitly set protocols. if (!channel.startsWith("tcp:") && !channel.startsWith("udp:") @@ -283,14 +280,16 @@ void GdbRemoteServerEngine::callTargetRemote() if (m_isQnxGdb) postCommand("target qnx " + channel, CB(handleTargetQnx)); + else if (m_isMulti) + postCommand("target extended-remote " + m_serverChannel, CB(handleTargetExtendedRemote)); 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()); - if (record.resultClass == GdbResultDone) { + if (response.resultClass == GdbResultDone) { // gdb server will stop the remote application itself. showMessage(_("INFERIOR STARTED")); showMessage(msgAttachedToStoppedInferior(), StatusBar); @@ -303,11 +302,50 @@ void GdbRemoteServerEngine::handleTargetRemote(const GdbResponse &record) } else { // 16^error,msg="hd:5555: Connection timed out." QString msg = msgConnectRemoteServerFailed( - QString::fromLocal8Bit(record.data.findChild("msg").data())); + QString::fromLocal8Bit(response.data.findChild("msg").data())); 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) { QTC_ASSERT(m_isQnxGdb, qDebug() << m_isQnxGdb); @@ -417,22 +455,36 @@ void GdbRemoteServerEngine::shutdownEngine() 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) { QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); DebuggerEngine::notifyEngineRemoteSetupDone(gdbServerPort, qmlPort); - if (qmlPort != -1) - startParameters().qmlServerPort = qmlPort; - if (gdbServerPort != -1) { - QString &rc = startParameters().remoteChannel; - const int sepIndex = rc.lastIndexOf(QLatin1Char(':')); - if (sepIndex != -1) { - rc.replace(sepIndex + 1, rc.count() - sepIndex - 1, - QString::number(gdbServerPort)); + if (m_isMulti) { + // Has been done in notifyEngineRemoteServerRunning + } else { + if (qmlPort != -1) + startParameters().qmlServerPort = qmlPort; + if (gdbServerPort != -1) { + QString &rc = startParameters().remoteChannel; + 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) diff --git a/src/plugins/debugger/gdb/remotegdbserveradapter.h b/src/plugins/debugger/gdb/remotegdbserveradapter.h index d0d4094028c..769c440dc89 100644 --- a/src/plugins/debugger/gdb/remotegdbserveradapter.h +++ b/src/plugins/debugger/gdb/remotegdbserveradapter.h @@ -70,20 +70,25 @@ signals: * a server start script should be used, but none is given. */ void requestSetup(); + void aboutToNotifyInferiorSetupOk(); private: Q_SLOT void readUploadStandardOutput(); Q_SLOT void readUploadStandardError(); Q_SLOT void uploadProcError(QProcess::ProcessError error); Q_SLOT void uploadProcFinished(); + Q_SLOT void callTargetRemote(); - virtual void notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort); - virtual void notifyEngineRemoteSetupFailed(const QString &reason); + void notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort); + void notifyEngineRemoteSetupFailed(const QString &reason); + void notifyEngineRemoteServerRunning(const QByteArray &serverChannel, int inferiorPid); + void notifyInferiorSetupOk(); void handleSetTargetAsync(const GdbResponse &response); void handleFileExecAndSymbols(const GdbResponse &response); - void callTargetRemote(); void handleTargetRemote(const GdbResponse &response); + void handleTargetExtendedRemote(const GdbResponse &response); + void handleTargetExtendedAttach(const GdbResponse &response); void handleTargetQnx(const GdbResponse &response); void handleAttach(const GdbResponse &response); void handleInterruptInferior(const GdbResponse &response); @@ -91,6 +96,9 @@ private: QProcess m_uploadProc; LocalGdbProcess m_gdbProc; + bool m_isMulti; + int m_targetPid; + QByteArray m_serverChannel; }; } // namespace Internal