From 82f4e82155bda4da3e7a3055b1b2662517440d7d Mon Sep 17 00:00:00 2001 From: Cody Northrop Date: Wed, 20 Sep 2017 10:04:20 -0600 Subject: [PATCH] Add version checking to Android workflow * Tag the RenderDoc layer with a version string that matches the host, including git hash. * In developer builds, check the version when scanning the application for RenderDoc support. * Pass the warning back to the UI to offer ways to fix. * Update APK patching to remove existing layer. --- qrenderdoc/Windows/Dialogs/CaptureDialog.cpp | 12 ++- renderdoc/api/replay/replay_enums.h | 9 +- renderdoc/core/android.cpp | 103 +++++++++++++++++-- 3 files changed, 111 insertions(+), 13 deletions(-) diff --git a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp index b777aa601..91239f699 100644 --- a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp @@ -430,8 +430,9 @@ void CaptureDialog::CheckAndroidSetup(QString &filename) const bool missingLibrary = bool(m_AndroidFlags & AndroidFlags::MissingLibrary); const bool missingPermissions = bool(m_AndroidFlags & AndroidFlags::MissingPermissions); + const bool wrongLayerVersion = bool(m_AndroidFlags & AndroidFlags::WrongLayerVersion); - if(missingLibrary || missingPermissions) + if(missingLibrary || missingPermissions || wrongLayerVersion) { // Check failed - set the warning visible GUIInvoke::call([this]() { @@ -463,6 +464,7 @@ void CaptureDialog::androidWarn_mouseClick() bool missingPermissions = bool(m_AndroidFlags & AndroidFlags::MissingPermissions); bool missingLibrary = bool(m_AndroidFlags & AndroidFlags::MissingLibrary); + bool wrongLayerVersion = bool(m_AndroidFlags & AndroidFlags::WrongLayerVersion); bool rootAccess = bool(m_AndroidFlags & AndroidFlags::RootAccess); if(missingPermissions) @@ -481,6 +483,14 @@ void CaptureDialog::androidWarn_mouseClick() "installed application.

"); } + if(wrongLayerVersion) + { + msg += + tr("Wrong layer version
" + "The RenderDoc library was found, but its version " + "does not match that of the host.

"); + } + if(missingPermissions) { // Don't prompt for patching if permissions are wrong - we can't fix that diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 9afb65323..314487224 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -3508,6 +3508,10 @@ DOCUMENT(R"(A set of flags giving details of the current status of Android traca The application is not debuggable. +.. data:: WrongLayerVersion + + The found RenderDoc layer does not match the server's version. + .. data:: RootAccess The device being targeted has root access. @@ -3522,8 +3526,9 @@ enum class AndroidFlags : uint32_t MissingLibrary = 0x1, MissingPermissions = 0x2, NotDebuggable = 0x4, - RootAccess = 0x8, - Unfixable = 0x10, + WrongLayerVersion = 0x8, + RootAccess = 0x10, + Unfixable = 0x20, }; BITMASK_OPERATORS(AndroidFlags); diff --git a/renderdoc/core/android.cpp b/renderdoc/core/android.cpp index 268aa2725..7b5a3d39c 100644 --- a/renderdoc/core/android.cpp +++ b/renderdoc/core/android.cpp @@ -28,6 +28,9 @@ #include "core/core.h" #include "strings/string_utils.h" +extern "C" RENDERDOC_API const char RENDERDOC_Version_Tag_String[] = + "RenderDoc_build_version: " FULL_VERSION_STRING " from git commit " GIT_COMMIT_HASH; + namespace Android { enum class ToolDir @@ -376,12 +379,13 @@ uint32_t StartAndroidPackageForCapture(const char *host, const char *package) return ret; } -bool SearchForAndroidLayer(const string &deviceID, const string &location, const string &layerName) +bool SearchForAndroidLayer(const string &deviceID, const string &location, const string &layerName, + string &foundLayer) { RDCLOG("Checking for layers in: %s", location.c_str()); - string findLayer = - adbExecCommand(deviceID, "shell find " + location + " -name " + layerName).strStdout; - if(!findLayer.empty()) + foundLayer = + trim(adbExecCommand(deviceID, "shell find " + location + " -name " + layerName).strStdout); + if(!foundLayer.empty()) { RDCLOG("Found RenderDoc layer in %s", location.c_str()); return true; @@ -444,6 +448,29 @@ bool AddLayerToAPK(const string &apk, const string &layerPath, const string &lay // Run aapt from the directory containing "lib" so the relative paths are good string relativeLayer("lib/" + abi + "/" + layerName); string workDir = removeFromEnd(layerPath, relativeLayer); + + // If the layer was already present in the APK, we need to remove it first + Process::ProcessResult contents = execCommand(aapt, "list \"" + apk + "\"", workDir); + if(contents.strStdout.empty()) + { + RDCERR("Failed to list contents of APK. STDERR: %s", contents.strStderror.c_str()); + return false; + } + + if(contents.strStdout.find(relativeLayer) != std::string::npos) + { + RDCLOG("Removing existing layer from APK before trying to add"); + Process::ProcessResult remove = + execCommand(aapt, "remove \"" + apk + "\" " + relativeLayer, workDir); + + if(!remove.strStdout.empty()) + { + RDCERR("Failed to remove existing layer from APK. STDERR: %s", remove.strStderror.c_str()); + return false; + } + } + + // Add the RenderDoc layer Process::ProcessResult result = execCommand(aapt, "add \"" + apk + "\" " + relativeLayer, workDir); if(result.strStdout.empty()) @@ -708,6 +735,49 @@ bool PullAPK(const string &deviceID, const string &pkgPath, const string &apk) return false; } +bool CheckLayerVersion(const string &deviceID, const string &layerName, const string &remoteLayer) +{ + RDCDEBUG("Checking layer version of: %s", layerName.c_str()); + + bool match = false; + + // Use 'strings' command on the device to find the layer's build version + // i.e. strings -n | grep + // Subtract 5 to provide a bit of wiggle room on version length + Process::ProcessResult result = adbExecCommand( + deviceID, "shell strings -n " + + StringFormat::Fmt("%u", strlen(RENDERDOC_Version_Tag_String) - 5) + " " + + remoteLayer + " | grep RenderDoc_build_version"); + + string line = trim(result.strStdout); + + if(line.empty()) + { + RDCLOG("RenderDoc layer is not versioned, so cannot be checked for compatibility."); + return false; + } + + std::vector vec; + split(line, vec, ' '); + string version = vec[1]; + string hash = vec[5]; + + if(version == FULL_VERSION_STRING && hash == GIT_COMMIT_HASH) + { + RDCLOG("RenderDoc layer version (%s) and git hash (%s) match.", version.c_str(), hash.c_str()); + match = true; + } + else + { + RDCLOG( + "RenderDoc layer version (%s) and git hash (%s) do NOT match the host version (%s) or git " + "hash (%s).", + version.c_str(), hash.c_str(), FULL_VERSION_STRING, GIT_COMMIT_HASH); + } + + return match; +} + bool CheckPermissions(const string &dump) { // TODO: remove this if we are sure that there are no permissions to check. @@ -1188,18 +1258,30 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage(const c *flags = AndroidFlags::NoFlags; bool found = false; + string layerPath = ""; - // First, see if the application contains the layer - if(SearchForAndroidLayer(deviceID, pkgPath, layerName)) + // Check a debug location only usable by rooted devices, overriding app's layer + if(SearchForAndroidLayer(deviceID, "/data/local/debug/vulkan", layerName, layerPath)) found = true; - // Next, check a debug location only usable by rooted devices - if(!found && SearchForAndroidLayer(deviceID, "/data/local/debug/vulkan", layerName)) + // See if the application contains the layer + if(!found && SearchForAndroidLayer(deviceID, pkgPath, layerName, layerPath)) found = true; // TODO: Add any future layer locations - if(!found) + if(found) + { +#if ENABLED(RDOC_DEVEL) + // Check the version of the layer found + if(!CheckLayerVersion(deviceID, layerName, layerPath)) + { + RDCWARN("RenderDoc layer found, but version does not match"); + *flags |= AndroidFlags::WrongLayerVersion; + } +#endif + } + else { RDCWARN("No RenderDoc layer for Vulkan or GLES was found"); *flags |= AndroidFlags::MissingLibrary; @@ -1255,7 +1337,8 @@ extern "C" RENDERDOC_API bool RENDERDOC_CC RENDERDOC_PushLayerToInstalledAndroid result = adbExecCommand(deviceID, "push " + layerPath + " " + layerDst); // Ensure the push succeeded - return SearchForAndroidLayer(deviceID, layerDst, layerName); + string foundLayer; + return SearchForAndroidLayer(deviceID, layerDst, layerName, foundLayer); } extern "C" RENDERDOC_API bool RENDERDOC_CC RENDERDOC_AddLayerToAndroidPackage(const char *host,