diff --git a/lldb/source/Plugins/Platform/Android/AdbClient.h b/lldb/source/Plugins/Platform/Android/AdbClient.h --- a/lldb/source/Plugins/Platform/Android/AdbClient.h +++ b/lldb/source/Plugins/Platform/Android/AdbClient.h @@ -36,20 +36,22 @@ friend class AdbClient; public: - ~SyncService(); + virtual ~SyncService(); - Status PullFile(const FileSpec &remote_file, const FileSpec &local_file); + virtual Status PullFile(const FileSpec &remote_file, + const FileSpec &local_file); Status PushFile(const FileSpec &local_file, const FileSpec &remote_file); - Status Stat(const FileSpec &remote_file, uint32_t &mode, uint32_t &size, - uint32_t &mtime); + virtual Status Stat(const FileSpec &remote_file, uint32_t &mode, + uint32_t &size, uint32_t &mtime); bool IsConnected() const; - private: + protected: explicit SyncService(std::unique_ptr &&conn); + private: Status SendSyncRequest(const char *request_id, const uint32_t data_len, const void *data); @@ -78,7 +80,7 @@ AdbClient(); explicit AdbClient(const std::string &device_id); - ~AdbClient(); + virtual ~AdbClient(); const std::string &GetDeviceID() const; @@ -96,10 +98,11 @@ Status Shell(const char *command, std::chrono::milliseconds timeout, std::string *output); - Status ShellToFile(const char *command, std::chrono::milliseconds timeout, - const FileSpec &output_file_spec); + virtual Status ShellToFile(const char *command, + std::chrono::milliseconds timeout, + const FileSpec &output_file_spec); - std::unique_ptr GetSyncService(Status &error); + virtual std::unique_ptr GetSyncService(Status &error); Status SwitchDeviceTransport(); diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h --- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h +++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h @@ -70,6 +70,9 @@ llvm::StringRef GetLibdlFunctionDeclarations(lldb_private::Process *process) override; + typedef std::unique_ptr AdbClientSP; + virtual AdbClientSP GetAdbClient(); + private: AdbClient::SyncService *GetSyncService(Status &error); diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp --- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp +++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp @@ -201,7 +201,7 @@ // mode == 0 can signify that adbd cannot access the file due security // constraints - try "cat ..." as a fallback. - AdbClient adb(m_device_id); + AdbClientSP adb(GetAdbClient()); char cmd[PATH_MAX]; if (const char *run_as = std::getenv("ANDROID_PLATFORM_RUN_AS")) @@ -210,7 +210,7 @@ else snprintf(cmd, sizeof(cmd), "cat '%s'", source_file.c_str()); - return adb.ShellToFile(cmd, minutes(1), destination); + return adb->ShellToFile(cmd, minutes(1), destination); } Status PlatformAndroid::PutFile(const FileSpec &source, @@ -254,7 +254,7 @@ if (pos != std::string::npos) source_file = source_file.substr(0, pos); - AdbClient adb(m_device_id); + AdbClientSP adb(GetAdbClient()); // Use 'shell dd' to download the file slice with the offset and size. char cmd[PATH_MAX]; @@ -263,7 +263,7 @@ "skip=%" PRIu64 " count=%" PRIu64 " status=none", source_file.c_str(), src_offset, src_size); - return adb.ShellToFile(cmd, minutes(1), dst_file_spec); + return adb->ShellToFile(cmd, minutes(1), dst_file_spec); } Status PlatformAndroid::DisconnectRemote() { @@ -287,9 +287,9 @@ return m_sdk_version; std::string version_string; - AdbClient adb(m_device_id); + AdbClientSP adb(GetAdbClient()); Status error = - adb.Shell("getprop ro.build.version.sdk", seconds(5), &version_string); + adb->Shell("getprop ro.build.version.sdk", seconds(5), &version_string); version_string = llvm::StringRef(version_string).trim().str(); if (error.Fail() || version_string.empty()) { @@ -325,9 +325,9 @@ nullptr) return Status("Symtab already available in the module"); - AdbClient adb(m_device_id); + AdbClientSP adb(GetAdbClient()); std::string tmpdir; - Status error = adb.Shell("mktemp --directory --tmpdir /data/local/tmp", + Status error = adb->Shell("mktemp --directory --tmpdir /data/local/tmp", seconds(5), &tmpdir); if (error.Fail() || tmpdir.empty()) return Status("Failed to generate temporary directory on the device (%s)", @@ -339,7 +339,7 @@ tmpdir_remover(&tmpdir, [&adb](std::string *s) { StreamString command; command.Printf("rm -rf %s", s->c_str()); - Status error = adb.Shell(command.GetData(), seconds(5), nullptr); + Status error = adb->Shell(command.GetData(), seconds(5), nullptr); Log *log = GetLog(LLDBLog::Platform); if (log && error.Fail()) @@ -355,7 +355,7 @@ command.Printf("oatdump --symbolize=%s --output=%s", module_sp->GetPlatformFileSpec().GetPath(false).c_str(), symfile_platform_filespec.GetPath(false).c_str()); - error = adb.Shell(command.GetData(), minutes(1), nullptr); + error = adb->Shell(command.GetData(), minutes(1), nullptr); if (error.Fail()) return Status("Oatdump failed: %s", error.AsCString()); @@ -394,11 +394,15 @@ return PlatformPOSIX::GetLibdlFunctionDeclarations(process); } +PlatformAndroid::AdbClientSP PlatformAndroid::GetAdbClient() { + return std::make_unique(m_device_id); +} + AdbClient::SyncService *PlatformAndroid::GetSyncService(Status &error) { if (m_adb_sync_svc && m_adb_sync_svc->IsConnected()) return m_adb_sync_svc.get(); - AdbClient adb(m_device_id); - m_adb_sync_svc = adb.GetSyncService(error); + AdbClientSP adb(GetAdbClient()); + m_adb_sync_svc = adb->GetSyncService(error); return (error.Success()) ? m_adb_sync_svc.get() : nullptr; } diff --git a/lldb/unittests/Platform/Android/CMakeLists.txt b/lldb/unittests/Platform/Android/CMakeLists.txt --- a/lldb/unittests/Platform/Android/CMakeLists.txt +++ b/lldb/unittests/Platform/Android/CMakeLists.txt @@ -2,6 +2,7 @@ add_lldb_unittest(AdbClientTests AdbClientTest.cpp + PlatformAndroidTest.cpp LINK_LIBS lldbPluginPlatformAndroid diff --git a/lldb/unittests/Platform/Android/PlatformAndroidTest.cpp b/lldb/unittests/Platform/Android/PlatformAndroidTest.cpp new file mode 100644 --- /dev/null +++ b/lldb/unittests/Platform/Android/PlatformAndroidTest.cpp @@ -0,0 +1,154 @@ +//===-- PlatformAndroidTest.cpp -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Plugins/Platform/Android/PlatformAndroid.h" +#include "Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h" +#include "TestingSupport/SubsystemRAII.h" +#include "TestingSupport/TestUtilities.h" +#include "lldb/Utility/Connection.h" +#include "gmock/gmock.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_android; +using namespace testing; + +namespace { + +class MockSyncService : public AdbClient::SyncService { +public: + MockSyncService() : SyncService(std::move(m_mock_conn)) {} + + MOCK_METHOD2(PullFile, + Status(const FileSpec &remote_file, const FileSpec &local_file)); + MOCK_METHOD4(Stat, Status(const FileSpec &remote_file, uint32_t &mode, + uint32_t &size, uint32_t &mtime)); + +private: + std::unique_ptr m_mock_conn; +}; + +typedef std::unique_ptr SyncServiceSP; + +class MockAdbClient : public AdbClient { +public: + explicit MockAdbClient() : AdbClient("mock") {} + + MOCK_METHOD3(ShellToFile, + Status(const char *command, std::chrono::milliseconds timeout, + const FileSpec &output_file_spec)); + MOCK_METHOD1(GetSyncService, SyncServiceSP(Status &error)); +}; + +class PlatformAndroidTest : public PlatformAndroid, public ::testing::Test { +public: + PlatformAndroidTest() : PlatformAndroid(false) { + m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer()); + } + + MOCK_METHOD0(GetAdbClient, AdbClientSP()); +}; + +} // namespace + +TEST_F(PlatformAndroidTest, DownloadModuleSliceWithNormalFile) { + auto sync_service = new MockSyncService(); + EXPECT_CALL(*sync_service, Stat(FileSpec("/system/lib64/libc.so"), _, _, _)) + .Times(1) + .WillOnce(DoAll(SetArgReferee<1>(1), Return(Status()))); + EXPECT_CALL(*sync_service, PullFile(FileSpec("/system/lib64/libc.so"), _)) + .Times(1) + .WillOnce(Return(Status())); + + auto adb_client = new MockAdbClient(); + EXPECT_CALL(*adb_client, GetSyncService(_)) + .Times(1) + .WillOnce(Return(ByMove(std::move(SyncServiceSP(sync_service))))); + + EXPECT_CALL(*this, GetAdbClient()) + .Times(1) + .WillOnce(Return(ByMove(std::move(AdbClientSP(adb_client))))); + + EXPECT_TRUE( + DownloadModuleSlice(FileSpec("/system/lib64/libc.so"), 0, 0, FileSpec()) + .Success()); +} + +TEST_F(PlatformAndroidTest, DownloadModuleSliceWithZipFile) { + auto adb_client = new MockAdbClient(); + EXPECT_CALL(*adb_client, + ShellToFile(StrEq("dd if='/system/app/Test/Test.apk' " + "iflag=skip_bytes,count_bytes " + "skip=4096 count=3600 status=none"), + _, _)) + .Times(1) + .WillOnce(Return(Status())); + + EXPECT_CALL(*this, GetAdbClient()) + .Times(1) + .WillOnce(Return(ByMove(std::move(AdbClientSP(adb_client))))); + + EXPECT_TRUE( + DownloadModuleSlice( + FileSpec("/system/app/Test/Test.apk!/lib/arm64-v8a/libtest.so"), 4096, + 3600, FileSpec()) + .Success()); +} + +TEST_F(PlatformAndroidTest, GetFileWithNormalFile) { + auto sync_service = new MockSyncService(); + EXPECT_CALL(*sync_service, Stat(FileSpec("/data/local/tmp/test"), _, _, _)) + .Times(1) + .WillOnce(DoAll(SetArgReferee<1>(1), Return(Status()))); + EXPECT_CALL(*sync_service, PullFile(FileSpec("/data/local/tmp/test"), _)) + .Times(1) + .WillOnce(Return(Status())); + + auto adb_client = new MockAdbClient(); + EXPECT_CALL(*adb_client, GetSyncService(_)) + .Times(1) + .WillOnce(Return(ByMove(std::move(SyncServiceSP(sync_service))))); + + EXPECT_CALL(*this, GetAdbClient()) + .Times(1) + .WillOnce(Return(ByMove(std::move(AdbClientSP(adb_client))))); + + EXPECT_TRUE(GetFile(FileSpec("/data/local/tmp/test"), FileSpec()).Success()); +} + +TEST_F(PlatformAndroidTest, GetFileWithCatFallback) { + auto sync_service = new MockSyncService(); + EXPECT_CALL( + *sync_service, + Stat(FileSpec("/data/data/com.example.app/lib-main/libtest.so"), _, _, _)) + .Times(1) + .WillOnce(DoAll(SetArgReferee<1>(0), Return(Status()))); + + auto adb_client0 = new MockAdbClient(); + EXPECT_CALL(*adb_client0, GetSyncService(_)) + .Times(1) + .WillOnce(Return(ByMove(std::move(SyncServiceSP(sync_service))))); + + auto adb_client1 = new MockAdbClient(); + EXPECT_CALL( + *adb_client1, + ShellToFile(StrEq("cat '/data/data/com.example.app/lib-main/libtest.so'"), + _, _)) + .Times(1) + .WillOnce(Return(Status())); + + EXPECT_CALL(*this, GetAdbClient()) + .Times(2) + .WillOnce(Return(ByMove(std::move(AdbClientSP(adb_client0))))) + .WillOnce(Return(ByMove(std::move(AdbClientSP(adb_client1))))); + + EXPECT_TRUE( + GetFile(FileSpec("/data/data/com.example.app/lib-main/libtest.so"), + FileSpec()) + .Success()); +}