From 25264d9bd904aaeaebe4f2f8b053fc1398793eb7 Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Mon, 1 Oct 2018 14:22:49 +0300 Subject: [PATCH] Fix debugging on Android arm64/x86_64 On Android 64, there is no lib/ symlink anymore, so we need to upload gdbserver from QtCreator. Change-Id: Ib6f6d9b623dc61b72dd434ce1b3b409e880bdeaa Reviewed-by: Vikas Pachdha --- src/libs/utils/synchronousprocess.cpp | 19 ++++++++++-- src/libs/utils/synchronousprocess.h | 2 +- src/plugins/android/androidconfigurations.cpp | 23 +++++++++++++- src/plugins/android/androidconfigurations.h | 1 + src/plugins/android/androidrunnerworker.cpp | 31 ++++++++++++++++--- src/plugins/android/androidrunnerworker.h | 4 ++- src/plugins/android/androidtoolchain.cpp | 14 +-------- src/plugins/debugger/debuggerengine.cpp | 2 +- 8 files changed, 71 insertions(+), 25 deletions(-) diff --git a/src/libs/utils/synchronousprocess.cpp b/src/libs/utils/synchronousprocess.cpp index 0f8d4ea8258..f9ab96eb478 100644 --- a/src/libs/utils/synchronousprocess.cpp +++ b/src/libs/utils/synchronousprocess.cpp @@ -443,7 +443,8 @@ static bool isGuiThread() } SynchronousProcessResponse SynchronousProcess::run(const QString &binary, - const QStringList &args) + const QStringList &args, + const QByteArray &writeData) { if (debug) qDebug() << '>' << Q_FUNC_INFO << binary << args; @@ -454,8 +455,20 @@ SynchronousProcessResponse SynchronousProcess::run(const QString &binary, // executable cannot be found in the path. Do not start the // event loop in that case. d->m_binary = binary; - d->m_process.start(binary, args, QIODevice::ReadOnly); - d->m_process.closeWriteChannel(); + d->m_process.start(binary, args, writeData.isEmpty() ? QIODevice::ReadOnly : QIODevice::ReadWrite); + connect(&d->m_process, &QProcess::started, this, [this, writeData] { + if (!writeData.isEmpty()) { + int pos = 0; + int sz = writeData.size(); + do { + d->m_process.waitForBytesWritten(); + auto res = d->m_process.write(writeData.constData() + pos, sz - pos); + if (res > 0) pos += res; + } while (pos < sz); + d->m_process.waitForBytesWritten(); + } + d->m_process.closeWriteChannel(); + }); if (!d->m_startFailure) { d->m_timer.start(); if (isGuiThread()) diff --git a/src/libs/utils/synchronousprocess.h b/src/libs/utils/synchronousprocess.h index b1901207d8e..d8e3275030b 100644 --- a/src/libs/utils/synchronousprocess.h +++ b/src/libs/utils/synchronousprocess.h @@ -127,7 +127,7 @@ public: ExitCodeInterpreter exitCodeInterpreter() const; // Starts an nested event loop and runs the binary with the arguments - SynchronousProcessResponse run(const QString &binary, const QStringList &args); + SynchronousProcessResponse run(const QString &binary, const QStringList &args, const QByteArray &writeData = {}); // Starts the binary with the arguments blocking the UI fully SynchronousProcessResponse runBlocking(const QString &binary, const QStringList &args); diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 0ceb88240f9..de738bee302 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -768,6 +768,27 @@ FileName AndroidConfig::ndkLocation() const return m_ndkLocation; } +static inline QString gdbServerArch(const Abi &abi) +{ + switch (abi.architecture()) { + case Abi::X86Architecture: + return abi.wordWidth() == 64 ? QString{"x86_64"} : QString{"x86"}; + case Abi::ArmArchitecture: + return abi.wordWidth() == 64 ? QString{"arm64"} : QString{"arm"}; + default: return {}; + }; +} + +FileName AndroidConfig::gdbServer(const ProjectExplorer::Abi &abi) const +{ + FileName path = AndroidConfigurations::currentConfig().ndkLocation(); + path.appendPath(QString::fromLatin1("prebuilt/android-%1/gdbserver/gdbserver") + .arg(gdbServerArch(abi))); + if (path.exists()) + return path; + return {}; +} + QVersionNumber AndroidConfig::ndkVersion() const { QVersionNumber version; @@ -1081,7 +1102,7 @@ void AndroidConfigurations::updateAutomaticKitList() QVariant id = Debugger::DebuggerItemManager::registerDebugger(debugger); Debugger::DebuggerKitInformation::setDebugger(toSetup, id); - AndroidGdbServerKitInformation::setGdbSever(toSetup, tc->suggestedGdbServer()); + AndroidGdbServerKitInformation::setGdbSever(toSetup, currentConfig().gdbServer(tc->targetAbi())); toSetup->makeSticky(); toSetup->setUnexpandedDisplayName(tr("Android for %1 (GCC %2, %3)") .arg(static_cast(qt)->targetArch()) diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index 941fab7fbd5..99544da511e 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -106,6 +106,7 @@ public: void setSdkManagerToolArgs(const QStringList &args); Utils::FileName ndkLocation() const; + Utils::FileName gdbServer(const ProjectExplorer::Abi &abi) const; QVersionNumber ndkVersion() const; void setNdkLocation(const Utils::FileName &ndkLocation); diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index f117d70c82e..b932e372a4a 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -29,6 +29,7 @@ #include "androidconstants.h" #include "androidmanager.h" #include "androidrunconfiguration.h" +#include "androidgdbserverkitinformation.h" #include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include #include @@ -222,6 +224,7 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa << "Extra Start Args:" << m_amStartExtraArgs << "Before Start ADB cmds:" << m_beforeStartAdbCommands << "After finish ADB cmds:" << m_afterFinishAdbCommands; + m_gdbserverPath = AndroidGdbServerKitInformation::gdbServer(target->kit()).toString(); } AndroidRunnerWorker::~AndroidRunnerWorker() @@ -255,13 +258,13 @@ bool AndroidRunnerWorker::adbShellAmNeedsQuotes() return !oldSdk; } -bool AndroidRunnerWorker::runAdb(const QStringList &args, int timeoutS) +bool AndroidRunnerWorker::runAdb(const QStringList &args, int timeoutS, const QByteArray &writeData) { QStringList adbArgs = selector() + args; qCDebug(androidRunWorkerLog) << "ADB command: " << m_adb << adbArgs.join(' '); Utils::SynchronousProcess adb; adb.setTimeoutS(timeoutS); - Utils::SynchronousProcessResponse response = adb.run(m_adb, adbArgs); + Utils::SynchronousProcessResponse response = adb.run(m_adb, adbArgs, writeData); m_lastRunAdbError = response.exitMessage(m_adb, timeoutS); m_lastRunAdbRawOutput = response.allRawOutput(); bool success = response.result == Utils::SynchronousProcessResponse::Finished; @@ -269,6 +272,18 @@ bool AndroidRunnerWorker::runAdb(const QStringList &args, int timeoutS) return success; } +bool AndroidRunnerWorker::uploadFile(const QString &from, const QString &to, const QString &flags) +{ + QFile f(from); + if (!f.open(QIODevice::ReadOnly)) + return false; + runAdb({"shell", "run-as", m_packageName, "rm", to}); + auto res = runAdb({"shell", "run-as", m_packageName, "sh", "-c", QString("'cat > %1'").arg(to)}, 60, f.readAll()); + if (!res) + return false; + return runAdb({"shell", "run-as", m_packageName, "chmod", flags, to}); +} + void AndroidRunnerWorker::adbKill(qint64 pid) { runAdb({"shell", "kill", "-9", QString::number(pid)}); @@ -411,9 +426,15 @@ void AndroidRunnerWorker::asyncStartHelper() runAdb({"shell", "run-as", m_packageName, "chmod", "a+x", packageDir}); QString gdbServerExecutable; + QString gdbServerPrefix = "./lib/"; if (!runAdb({"shell", "run-as", m_packageName, "ls", "lib/"})) { - emit remoteProcessFinished(tr("Failed to get process path. Reason: %1.").arg(m_lastRunAdbError)); - return; + if (m_gdbserverPath.isEmpty()) { + emit remoteProcessFinished(tr("Failed to get process path. Reason: %1.").arg(m_lastRunAdbError)); + return; + } + uploadFile(m_gdbserverPath, "gdbserver"); + runAdb({"shell", "run-as", m_packageName, "ls"}); + gdbServerPrefix = "./"; } for (const auto &line: m_lastRunAdbRawOutput.split('\n')) { @@ -433,7 +454,7 @@ void AndroidRunnerWorker::asyncStartHelper() runAdb({"shell", "run-as", m_packageName, "rm", gdbServerSocket}); std::unique_ptr gdbServerProcess(new QProcess, deleter); gdbServerProcess->start(m_adb, selector() << "shell" << "run-as" - << m_packageName << "lib/" + gdbServerExecutable + << m_packageName << gdbServerPrefix + gdbServerExecutable << "--multi" << "+" + gdbServerSocket); if (!gdbServerProcess->waitForStarted()) { emit remoteProcessFinished(tr("Failed to start C++ debugger.")); diff --git a/src/plugins/android/androidrunnerworker.h b/src/plugins/android/androidrunnerworker.h index c9714724ccf..9c2bbf1f1be 100644 --- a/src/plugins/android/androidrunnerworker.h +++ b/src/plugins/android/androidrunnerworker.h @@ -47,7 +47,8 @@ public: AndroidRunnerWorker(ProjectExplorer::RunWorker *runner, const QString &packageName); ~AndroidRunnerWorker() override; bool adbShellAmNeedsQuotes(); - bool runAdb(const QStringList &args, int timeoutS = 10); + bool runAdb(const QStringList &args, int timeoutS = 10, const QByteArray &writeData = {}); + bool uploadFile(const QString &from, const QString &to, const QString &flags = "+x"); void adbKill(qint64 pid); QStringList selector() const; void forceStop(); @@ -110,6 +111,7 @@ protected: int m_apiLevel = -1; QString m_extraAppParams; Utils::Environment m_extraEnvVars; + QString m_gdbserverPath; }; } // namespace Internal diff --git a/src/plugins/android/androidtoolchain.cpp b/src/plugins/android/androidtoolchain.cpp index 9540b162408..7b32e5be69c 100644 --- a/src/plugins/android/androidtoolchain.cpp +++ b/src/plugins/android/androidtoolchain.cpp @@ -193,19 +193,7 @@ FileName AndroidToolChain::suggestedDebugger() const FileName AndroidToolChain::suggestedGdbServer() const { - FileName path = AndroidConfigurations::currentConfig().ndkLocation(); - path.appendPath(QString::fromLatin1("prebuilt/android-%1/gdbserver/gdbserver") - .arg(Abi::toString(targetAbi().architecture()))); - if (path.exists()) - return path; - path = AndroidConfigurations::currentConfig().ndkLocation(); - path.appendPath(QString::fromLatin1("toolchains/%1-%2/prebuilt/gdbserver") - .arg(AndroidConfig::toolchainPrefix(targetAbi())) - .arg(m_ndkToolChainVersion)); - if (path.exists()) - return path; - - return FileName(); + return AndroidConfigurations::currentConfig().gdbServer(targetAbi()); } QVariantMap AndroidToolChain::toMap() const diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index d18cfd3f54c..18092c8099e 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -2529,7 +2529,7 @@ Context CppDebuggerEngine::languageContext() const void CppDebuggerEngine::validateExecutable() { DebuggerRunParameters &rp = mutableRunParameters(); - const bool warnOnRelease = boolSetting(WarnOnReleaseBuilds); + const bool warnOnRelease = boolSetting(WarnOnReleaseBuilds) && rp.toolChainAbi.osFlavor() != Abi::AndroidLinuxFlavor; bool warnOnInappropriateDebugger = false; QString detailedWarning; switch (rp.toolChainAbi.binaryFormat()) {