diff options
Diffstat (limited to 'source/Plugins/Platform/Android')
-rw-r--r-- | source/Plugins/Platform/Android/AdbClient.cpp | 569 | ||||
-rw-r--r-- | source/Plugins/Platform/Android/AdbClient.h | 132 | ||||
-rw-r--r-- | source/Plugins/Platform/Android/CMakeLists.txt | 5 | ||||
-rw-r--r-- | source/Plugins/Platform/Android/Makefile | 14 | ||||
-rw-r--r-- | source/Plugins/Platform/Android/PlatformAndroid.cpp | 389 | ||||
-rw-r--r-- | source/Plugins/Platform/Android/PlatformAndroid.h | 114 | ||||
-rw-r--r-- | source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp | 275 | ||||
-rw-r--r-- | source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h | 79 |
8 files changed, 1577 insertions, 0 deletions
diff --git a/source/Plugins/Platform/Android/AdbClient.cpp b/source/Plugins/Platform/Android/AdbClient.cpp new file mode 100644 index 0000000000000..736447fd22d21 --- /dev/null +++ b/source/Plugins/Platform/Android/AdbClient.cpp @@ -0,0 +1,569 @@ +//===-- AdbClient.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Other libraries and framework includes +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataEncoder.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/FileUtilities.h" + +// Project includes +#include "AdbClient.h" + +#include <limits.h> + +#include <algorithm> +#include <fstream> +#include <sstream> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_android; + +namespace { + +const uint32_t kReadTimeout = 1000000; // 1 second +const char * kOKAY = "OKAY"; +const char * kFAIL = "FAIL"; +const char * kDATA = "DATA"; +const char * kDONE = "DONE"; + +const char * kSEND = "SEND"; +const char * kRECV = "RECV"; +const char * kSTAT = "STAT"; + +const size_t kSyncPacketLen = 8; +// Maximum size of a filesync DATA packet. +const size_t kMaxPushData = 2*1024; +// Default mode for pushed files. +const uint32_t kDefaultMode = 0100770; // S_IFREG | S_IRWXU | S_IRWXG + +const char * kSocketNamespaceAbstract = "localabstract"; +const char * kSocketNamespaceFileSystem = "localfilesystem"; + +} // namespace + +Error +AdbClient::CreateByDeviceID(const std::string &device_id, AdbClient &adb) +{ + DeviceIDList connect_devices; + auto error = adb.GetDevices(connect_devices); + if (error.Fail()) + return error; + + if (device_id.empty()) + { + if (connect_devices.size() != 1) + return Error("Expected a single connected device, got instead %" PRIu64, + static_cast<uint64_t>(connect_devices.size())); + + adb.SetDeviceID(connect_devices.front()); + } + else + { + auto find_it = std::find(connect_devices.begin(), connect_devices.end(), device_id); + if (find_it == connect_devices.end()) + return Error("Device \"%s\" not found", device_id.c_str()); + + adb.SetDeviceID(*find_it); + } + return error; +} + +AdbClient::AdbClient (const std::string &device_id) + : m_device_id (device_id) +{ +} + +void +AdbClient::SetDeviceID (const std::string &device_id) +{ + m_device_id = device_id; +} + +const std::string& +AdbClient::GetDeviceID() const +{ + return m_device_id; +} + +Error +AdbClient::Connect () +{ + Error error; + m_conn.Connect ("connect://localhost:5037", &error); + + return error; +} + +Error +AdbClient::GetDevices (DeviceIDList &device_list) +{ + device_list.clear (); + + auto error = SendMessage ("host:devices"); + if (error.Fail ()) + return error; + + error = ReadResponseStatus (); + if (error.Fail ()) + return error; + + std::vector<char> in_buffer; + error = ReadMessage (in_buffer); + + llvm::StringRef response (&in_buffer[0], in_buffer.size ()); + llvm::SmallVector<llvm::StringRef, 4> devices; + response.split (devices, "\n", -1, false); + + for (const auto device: devices) + device_list.push_back (device.split ('\t').first); + + return error; +} + +Error +AdbClient::SetPortForwarding (const uint16_t local_port, const uint16_t remote_port) +{ + char message[48]; + snprintf (message, sizeof (message), "forward:tcp:%d;tcp:%d", local_port, remote_port); + + const auto error = SendDeviceMessage (message); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::SetPortForwarding (const uint16_t local_port, + const char* remote_socket_name, + const UnixSocketNamespace socket_namespace) +{ + char message[PATH_MAX]; + const char * sock_namespace_str = (socket_namespace == UnixSocketNamespaceAbstract) ? + kSocketNamespaceAbstract : kSocketNamespaceFileSystem; + snprintf (message, sizeof (message), "forward:tcp:%d;%s:%s", + local_port, + sock_namespace_str, + remote_socket_name); + + const auto error = SendDeviceMessage (message); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::DeletePortForwarding (const uint16_t local_port) +{ + char message[32]; + snprintf (message, sizeof (message), "killforward:tcp:%d", local_port); + + const auto error = SendDeviceMessage (message); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::SendMessage (const std::string &packet, const bool reconnect) +{ + Error error; + if (reconnect) + { + error = Connect (); + if (error.Fail ()) + return error; + } + + char length_buffer[5]; + snprintf (length_buffer, sizeof (length_buffer), "%04x", static_cast<int>(packet.size ())); + + ConnectionStatus status; + + m_conn.Write (length_buffer, 4, status, &error); + if (error.Fail ()) + return error; + + m_conn.Write (packet.c_str (), packet.size (), status, &error); + return error; +} + +Error +AdbClient::SendDeviceMessage (const std::string &packet) +{ + std::ostringstream msg; + msg << "host-serial:" << m_device_id << ":" << packet; + return SendMessage (msg.str ()); +} + +Error +AdbClient::ReadMessage (std::vector<char> &message) +{ + message.clear (); + + char buffer[5]; + buffer[4] = 0; + + auto error = ReadAllBytes (buffer, 4); + if (error.Fail ()) + return error; + + unsigned int packet_len = 0; + sscanf (buffer, "%x", &packet_len); + + message.resize (packet_len, 0); + error = ReadAllBytes (&message[0], packet_len); + if (error.Fail ()) + message.clear (); + + return error; +} + +Error +AdbClient::ReadMessageStream (std::vector<char>& message, uint32_t timeout_ms) +{ + auto start = std::chrono::steady_clock::now(); + message.clear(); + + Error error; + lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess; + char buffer[1024]; + while (error.Success() && status == lldb::eConnectionStatusSuccess) + { + auto end = std::chrono::steady_clock::now(); + uint32_t elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(); + if (elapsed_time >= timeout_ms) + return Error("Timed out"); + + size_t n = m_conn.Read(buffer, sizeof(buffer), 1000 * (timeout_ms - elapsed_time), status, &error); + if (n > 0) + message.insert(message.end(), &buffer[0], &buffer[n]); + } + return error; +} + +Error +AdbClient::ReadResponseStatus() +{ + char response_id[5]; + + static const size_t packet_len = 4; + response_id[packet_len] = 0; + + auto error = ReadAllBytes (response_id, packet_len); + if (error.Fail ()) + return error; + + if (strncmp (response_id, kOKAY, packet_len) != 0) + return GetResponseError (response_id); + + return error; +} + +Error +AdbClient::GetResponseError (const char *response_id) +{ + if (strcmp (response_id, kFAIL) != 0) + return Error ("Got unexpected response id from adb: \"%s\"", response_id); + + std::vector<char> error_message; + auto error = ReadMessage (error_message); + if (error.Success ()) + error.SetErrorString (std::string (&error_message[0], error_message.size ()).c_str ()); + + return error; +} + +Error +AdbClient::SwitchDeviceTransport () +{ + std::ostringstream msg; + msg << "host:transport:" << m_device_id; + + auto error = SendMessage (msg.str ()); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::PullFile (const FileSpec &remote_file, const FileSpec &local_file) +{ + auto error = StartSync (); + if (error.Fail ()) + return error; + + const auto local_file_path = local_file.GetPath (); + llvm::FileRemover local_file_remover (local_file_path.c_str ()); + + std::ofstream dst (local_file_path, std::ios::out | std::ios::binary); + if (!dst.is_open ()) + return Error ("Unable to open local file %s", local_file_path.c_str()); + + const auto remote_file_path = remote_file.GetPath (false); + error = SendSyncRequest (kRECV, remote_file_path.length (), remote_file_path.c_str ()); + if (error.Fail ()) + return error; + + std::vector<char> chunk; + bool eof = false; + while (!eof) + { + error = PullFileChunk (chunk, eof); + if (error.Fail ()) + return error; + if (!eof) + dst.write (&chunk[0], chunk.size ()); + } + + local_file_remover.releaseFile (); + return error; +} + +Error +AdbClient::PushFile (const FileSpec &local_file, const FileSpec &remote_file) +{ + auto error = StartSync (); + if (error.Fail ()) + return error; + + const auto local_file_path (local_file.GetPath ()); + std::ifstream src (local_file_path.c_str(), std::ios::in | std::ios::binary); + if (!src.is_open ()) + return Error ("Unable to open local file %s", local_file_path.c_str()); + + std::stringstream file_description; + file_description << remote_file.GetPath(false).c_str() << "," << kDefaultMode; + std::string file_description_str = file_description.str(); + error = SendSyncRequest (kSEND, file_description_str.length(), file_description_str.c_str()); + if (error.Fail ()) + return error; + + char chunk[kMaxPushData]; + while (!src.eof() && !src.read(chunk, kMaxPushData).bad()) + { + size_t chunk_size = src.gcount(); + error = SendSyncRequest(kDATA, chunk_size, chunk); + if (error.Fail ()) + return Error ("Failed to send file chunk: %s", error.AsCString ()); + } + error = SendSyncRequest(kDONE, local_file.GetModificationTime().seconds(), nullptr); + if (error.Fail ()) + return error; + + std::string response_id; + uint32_t data_len; + error = ReadSyncHeader (response_id, data_len); + if (error.Fail ()) + return Error ("Failed to read DONE response: %s", error.AsCString ()); + if (response_id == kFAIL) + { + std::string error_message (data_len, 0); + error = ReadAllBytes (&error_message[0], data_len); + if (error.Fail ()) + return Error ("Failed to read DONE error message: %s", error.AsCString ()); + return Error ("Failed to push file: %s", error_message.c_str ()); + } + else if (response_id != kOKAY) + return Error ("Got unexpected DONE response: %s", response_id.c_str ()); + + // If there was an error reading the source file, finish the adb file + // transfer first so that adb isn't expecting any more data. + if (src.bad()) + return Error ("Failed read on %s", local_file_path.c_str()); + return error; +} + +Error +AdbClient::StartSync () +{ + auto error = SwitchDeviceTransport (); + if (error.Fail ()) + return Error ("Failed to switch to device transport: %s", error.AsCString ()); + + error = Sync (); + if (error.Fail ()) + return Error ("Sync failed: %s", error.AsCString ()); + + return error; +} + +Error +AdbClient::Sync () +{ + auto error = SendMessage ("sync:", false); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::PullFileChunk (std::vector<char> &buffer, bool &eof) +{ + buffer.clear (); + + std::string response_id; + uint32_t data_len; + auto error = ReadSyncHeader (response_id, data_len); + if (error.Fail ()) + return error; + + if (response_id == kDATA) + { + buffer.resize (data_len, 0); + error = ReadAllBytes (&buffer[0], data_len); + if (error.Fail ()) + buffer.clear (); + } + else if (response_id == kDONE) + { + eof = true; + } + else if (response_id == kFAIL) + { + std::string error_message (data_len, 0); + error = ReadAllBytes (&error_message[0], data_len); + if (error.Fail ()) + return Error ("Failed to read pull error message: %s", error.AsCString ()); + return Error ("Failed to pull file: %s", error_message.c_str ()); + } + else + return Error ("Pull failed with unknown response: %s", response_id.c_str ()); + + return Error (); +} + +Error +AdbClient::SendSyncRequest (const char *request_id, const uint32_t data_len, const void *data) +{ + const DataBufferSP data_sp (new DataBufferHeap (kSyncPacketLen, 0)); + DataEncoder encoder (data_sp, eByteOrderLittle, sizeof (void*)); + auto offset = encoder.PutData (0, request_id, strlen(request_id)); + encoder.PutU32 (offset, data_len); + + Error error; + ConnectionStatus status; + m_conn.Write (data_sp->GetBytes (), kSyncPacketLen, status, &error); + if (error.Fail ()) + return error; + + if (data) + m_conn.Write (data, data_len, status, &error); + return error; +} + +Error +AdbClient::ReadSyncHeader (std::string &response_id, uint32_t &data_len) +{ + char buffer[kSyncPacketLen]; + + auto error = ReadAllBytes (buffer, kSyncPacketLen); + if (error.Success ()) + { + response_id.assign (&buffer[0], 4); + DataExtractor extractor (&buffer[4], 4, eByteOrderLittle, sizeof (void*)); + offset_t offset = 0; + data_len = extractor.GetU32 (&offset); + } + + return error; +} + +Error +AdbClient::ReadAllBytes (void *buffer, size_t size) +{ + Error error; + ConnectionStatus status; + char *read_buffer = static_cast<char*>(buffer); + + size_t tota_read_bytes = 0; + while (tota_read_bytes < size) + { + auto read_bytes = m_conn.Read (read_buffer + tota_read_bytes, size - tota_read_bytes, kReadTimeout, status, &error); + if (error.Fail ()) + return error; + tota_read_bytes += read_bytes; + } + return error; +} + +Error +AdbClient::Stat (const FileSpec &remote_file, uint32_t &mode, uint32_t &size, uint32_t &mtime) +{ + auto error = StartSync (); + if (error.Fail ()) + return error; + + const std::string remote_file_path (remote_file.GetPath (false)); + error = SendSyncRequest (kSTAT, remote_file_path.length (), remote_file_path.c_str ()); + if (error.Fail ()) + return Error ("Failed to send request: %s", error.AsCString ()); + + static const size_t stat_len = strlen (kSTAT); + static const size_t response_len = stat_len + (sizeof (uint32_t) * 3); + + std::vector<char> buffer (response_len); + error = ReadAllBytes (&buffer[0], buffer.size ()); + if (error.Fail ()) + return Error ("Failed to read response: %s", error.AsCString ()); + + DataExtractor extractor (&buffer[0], buffer.size (), eByteOrderLittle, sizeof (void*)); + offset_t offset = 0; + + const void* command = extractor.GetData (&offset, stat_len); + if (!command) + return Error ("Failed to get response command"); + const char* command_str = static_cast<const char*> (command); + if (strncmp (command_str, kSTAT, stat_len)) + return Error ("Got invalid stat command: %s", command_str); + + mode = extractor.GetU32 (&offset); + size = extractor.GetU32 (&offset); + mtime = extractor.GetU32 (&offset); + return Error (); +} + +Error +AdbClient::Shell (const char* command, uint32_t timeout_ms, std::string* output) +{ + auto error = SwitchDeviceTransport (); + if (error.Fail ()) + return Error ("Failed to switch to device transport: %s", error.AsCString ()); + + StreamString adb_command; + adb_command.Printf("shell:%s", command); + error = SendMessage (adb_command.GetData(), false); + if (error.Fail ()) + return error; + + error = ReadResponseStatus (); + if (error.Fail ()) + return error; + + std::vector<char> in_buffer; + error = ReadMessageStream (in_buffer, timeout_ms); + if (error.Fail()) + return error; + + if (output) + output->assign(in_buffer.begin(), in_buffer.end()); + return error; +} diff --git a/source/Plugins/Platform/Android/AdbClient.h b/source/Plugins/Platform/Android/AdbClient.h new file mode 100644 index 0000000000000..4ec411d1411dd --- /dev/null +++ b/source/Plugins/Platform/Android/AdbClient.h @@ -0,0 +1,132 @@ +//===-- AdbClient.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AdbClient_h_ +#define liblldb_AdbClient_h_ + +// C Includes + +// C++ Includes + +#include <list> +#include <string> +#include <vector> + +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/Error.h" +#include "lldb/Host/ConnectionFileDescriptor.h" + +namespace lldb_private { + +class FileSpec; + +namespace platform_android { + +class AdbClient +{ +public: + enum UnixSocketNamespace + { + UnixSocketNamespaceAbstract, + UnixSocketNamespaceFileSystem, + }; + + using DeviceIDList = std::list<std::string>; + + static Error + CreateByDeviceID(const std::string &device_id, AdbClient &adb); + + AdbClient () = default; + explicit AdbClient (const std::string &device_id); + + const std::string& + GetDeviceID() const; + + Error + GetDevices (DeviceIDList &device_list); + + Error + SetPortForwarding (const uint16_t local_port, const uint16_t remote_port); + + Error + SetPortForwarding (const uint16_t local_port, + const char* remote_socket_name, + const UnixSocketNamespace socket_namespace); + + Error + DeletePortForwarding (const uint16_t local_port); + + Error + PullFile (const FileSpec &remote_file, const FileSpec &local_file); + + Error + PushFile (const FileSpec &local_file, const FileSpec &remote_file); + + Error + Stat (const FileSpec &remote_file, uint32_t &mode, uint32_t &size, uint32_t &mtime); + + Error + Shell (const char* command, uint32_t timeout_ms, std::string* output); + +private: + Error + Connect (); + + void + SetDeviceID (const std::string &device_id); + + Error + SendMessage (const std::string &packet, const bool reconnect = true); + + Error + SendDeviceMessage (const std::string &packet); + + Error + SendSyncRequest (const char *request_id, const uint32_t data_len, const void *data); + + Error + ReadSyncHeader (std::string &response_id, uint32_t &data_len); + + Error + ReadMessage (std::vector<char> &message); + + Error + ReadMessageStream (std::vector<char> &message, uint32_t timeout_ms); + + Error + GetResponseError (const char *response_id); + + Error + ReadResponseStatus (); + + Error + SwitchDeviceTransport (); + + Error + Sync (); + + Error + StartSync (); + + Error + PullFileChunk (std::vector<char> &buffer, bool &eof); + + Error + ReadAllBytes (void *buffer, size_t size); + + std::string m_device_id; + ConnectionFileDescriptor m_conn; +}; + +} // namespace platform_android +} // namespace lldb_private + +#endif // liblldb_AdbClient_h_ diff --git a/source/Plugins/Platform/Android/CMakeLists.txt b/source/Plugins/Platform/Android/CMakeLists.txt new file mode 100644 index 0000000000000..e831a33a4b6d4 --- /dev/null +++ b/source/Plugins/Platform/Android/CMakeLists.txt @@ -0,0 +1,5 @@ +add_lldb_library(lldbPluginPlatformAndroid + AdbClient.cpp + PlatformAndroid.cpp + PlatformAndroidRemoteGDBServer.cpp + ) diff --git a/source/Plugins/Platform/Android/Makefile b/source/Plugins/Platform/Android/Makefile new file mode 100644 index 0000000000000..aa186f924e66c --- /dev/null +++ b/source/Plugins/Platform/Android/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/Android/Makefile ------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginPlatformAndroid +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Android/PlatformAndroid.cpp b/source/Plugins/Platform/Android/PlatformAndroid.cpp new file mode 100644 index 0000000000000..e842884c046a3 --- /dev/null +++ b/source/Plugins/Platform/Android/PlatformAndroid.cpp @@ -0,0 +1,389 @@ +//===-- PlatformAndroid.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/StringConvert.h" +#include "Utility/UriParser.h" + +// Project includes +#include "AdbClient.h" +#include "PlatformAndroid.h" +#include "PlatformAndroidRemoteGDBServer.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_android; + +static uint32_t g_initialize_count = 0; +static const unsigned int g_android_default_cache_size = 2048; // Fits inside 4k adb packet. + +void +PlatformAndroid::Initialize () +{ + PlatformLinux::Initialize (); + + if (g_initialize_count++ == 0) + { +#if defined(__ANDROID__) + PlatformSP default_platform_sp (new PlatformAndroid(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform (default_platform_sp); +#endif + PluginManager::RegisterPlugin (PlatformAndroid::GetPluginNameStatic(false), + PlatformAndroid::GetPluginDescriptionStatic(false), + PlatformAndroid::CreateInstance); + } +} + +void +PlatformAndroid::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformAndroid::CreateInstance); + } + } + + PlatformLinux::Terminate (); +} + +PlatformSP +PlatformAndroid::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = "<null>"; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>"; + + log->Printf ("PlatformAndroid::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::PC: + create = true; + break; + +#if defined(__ANDROID__) + // Only accept "unknown" for the vendor if the host is android and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified_ + case llvm::Triple::VendorType::UnknownVendor: + create = !arch->TripleVendorWasSpecified(); + break; +#endif + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::Android: + break; + +#if defined(__ANDROID__) + // Only accept "unknown" for the OS if the host is android and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::OSType::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + + if (create) + { + if (log) + log->Printf ("PlatformAndroid::%s() creating remote-android platform", __FUNCTION__); + return PlatformSP(new PlatformAndroid(false)); + } + + if (log) + log->Printf ("PlatformAndroid::%s() aborting creation of remote-android platform", __FUNCTION__); + + return PlatformSP(); +} + +PlatformAndroid::PlatformAndroid (bool is_host) : + PlatformLinux(is_host), + m_sdk_version(0) +{ +} + +PlatformAndroid::~PlatformAndroid() +{ +} + +ConstString +PlatformAndroid::GetPluginNameStatic (bool is_host) +{ + if (is_host) + { + static ConstString g_host_name(Platform::GetHostPlatformName ()); + return g_host_name; + } + else + { + static ConstString g_remote_name("remote-android"); + return g_remote_name; + } +} + +const char * +PlatformAndroid::GetPluginDescriptionStatic (bool is_host) +{ + if (is_host) + return "Local Android user platform plug-in."; + else + return "Remote Android user platform plug-in."; +} + +ConstString +PlatformAndroid::GetPluginName() +{ + return GetPluginNameStatic(IsHost()); +} + +Error +PlatformAndroid::ConnectRemote(Args& args) +{ + m_device_id.clear(); + + if (IsHost()) + { + return Error ("can't connect to the host platform '%s', always connected", GetPluginName().GetCString()); + } + + if (!m_remote_platform_sp) + m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer()); + + int port; + std::string scheme, host, path; + const char *url = args.GetArgumentAtIndex(0); + if (!url) + return Error("URL is null."); + if (!UriParser::Parse(url, scheme, host, port, path)) + return Error("Invalid URL: %s", url); + if (host != "localhost") + m_device_id = host; + + auto error = PlatformLinux::ConnectRemote(args); + if (error.Success()) + { + AdbClient adb; + error = AdbClient::CreateByDeviceID(m_device_id, adb); + if (error.Fail()) + return error; + + m_device_id = adb.GetDeviceID(); + } + return error; +} + +Error +PlatformAndroid::GetFile (const FileSpec& source, + const FileSpec& destination) +{ + if (IsHost() || !m_remote_platform_sp) + return PlatformLinux::GetFile(source, destination); + + FileSpec source_spec (source.GetPath (false), false, FileSpec::ePathSyntaxPosix); + if (source_spec.IsRelative()) + source_spec = GetRemoteWorkingDirectory ().CopyByAppendingPathComponent (source_spec.GetCString (false)); + + AdbClient adb (m_device_id); + return adb.PullFile (source_spec, destination); +} + +Error +PlatformAndroid::PutFile (const FileSpec& source, + const FileSpec& destination, + uint32_t uid, + uint32_t gid) +{ + if (IsHost() || !m_remote_platform_sp) + return PlatformLinux::PutFile (source, destination, uid, gid); + + FileSpec destination_spec (destination.GetPath (false), false, FileSpec::ePathSyntaxPosix); + if (destination_spec.IsRelative()) + destination_spec = GetRemoteWorkingDirectory ().CopyByAppendingPathComponent (destination_spec.GetCString (false)); + + AdbClient adb (m_device_id); + // TODO: Set correct uid and gid on remote file. + return adb.PushFile(source, destination_spec); +} + +const char * +PlatformAndroid::GetCacheHostname () +{ + return m_device_id.c_str (); +} + +Error +PlatformAndroid::DownloadModuleSlice (const FileSpec &src_file_spec, + const uint64_t src_offset, + const uint64_t src_size, + const FileSpec &dst_file_spec) +{ + if (src_offset != 0) + return Error ("Invalid offset - %" PRIu64, src_offset); + + return GetFile (src_file_spec, dst_file_spec); +} + +Error +PlatformAndroid::DisconnectRemote() +{ + Error error = PlatformLinux::DisconnectRemote(); + if (error.Success()) + { + m_device_id.clear(); + m_sdk_version = 0; + } + return error; +} + +uint32_t +PlatformAndroid::GetDefaultMemoryCacheLineSize() +{ + return g_android_default_cache_size; +} + +uint32_t +PlatformAndroid::GetSdkVersion() +{ + if (!IsConnected()) + return 0; + + if (m_sdk_version != 0) + return m_sdk_version; + + std::string version_string; + AdbClient adb(m_device_id); + Error error = adb.Shell("getprop ro.build.version.sdk", 5000 /* ms */, &version_string); + version_string = llvm::StringRef(version_string).trim().str(); + + if (error.Fail() || version_string.empty()) + { + Log* log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM); + if (log) + log->Printf("Get SDK version failed. (error: %s, output: %s)", + error.AsCString(), version_string.c_str()); + return 0; + } + + m_sdk_version = StringConvert::ToUInt32(version_string.c_str()); + return m_sdk_version; +} + +Error +PlatformAndroid::DownloadSymbolFile (const lldb::ModuleSP& module_sp, + const FileSpec& dst_file_spec) +{ + // For oat file we can try to fetch additional debug info from the device + if (module_sp->GetFileSpec().GetFileNameExtension() != ConstString("oat")) + return Error("Symbol file downloading only supported for oat files"); + + // If we have no information about the platform file we can't execute oatdump + if (!module_sp->GetPlatformFileSpec()) + return Error("No platform file specified"); + + // Symbolizer isn't available before SDK version 23 + if (GetSdkVersion() < 23) + return Error("Symbol file generation only supported on SDK 23+"); + + // If we already have symtab then we don't have to try and generate one + if (module_sp->GetSectionList()->FindSectionByName(ConstString(".symtab")) != nullptr) + return Error("Symtab already available in the module"); + + AdbClient adb(m_device_id); + + std::string tmpdir; + Error error = adb.Shell("mktemp --directory --tmpdir /data/local/tmp", 5000 /* ms */, &tmpdir); + if (error.Fail() || tmpdir.empty()) + return Error("Failed to generate temporary directory on the device (%s)", error.AsCString()); + tmpdir = llvm::StringRef(tmpdir).trim().str(); + + // Create file remover for the temporary directory created on the device + std::unique_ptr<std::string, std::function<void(std::string*)>> tmpdir_remover( + &tmpdir, + [this, &adb](std::string* s) { + StreamString command; + command.Printf("rm -rf %s", s->c_str()); + Error error = adb.Shell(command.GetData(), 5000 /* ms */, nullptr); + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (error.Fail()) + log->Printf("Failed to remove temp directory: %s", error.AsCString()); + } + ); + + FileSpec symfile_platform_filespec(tmpdir.c_str(), false); + symfile_platform_filespec.AppendPathComponent("symbolized.oat"); + + // Execute oatdump on the remote device to generate a file with symtab + StreamString command; + command.Printf("oatdump --symbolize=%s --output=%s", + module_sp->GetPlatformFileSpec().GetCString(false), + symfile_platform_filespec.GetCString(false)); + error = adb.Shell(command.GetData(), 60000 /* ms */, nullptr); + if (error.Fail()) + return Error("Oatdump failed: %s", error.AsCString()); + + // Download the symbolfile from the remote device + return GetFile(symfile_platform_filespec, dst_file_spec); +} + +bool +PlatformAndroid::GetRemoteOSVersion () +{ + m_major_os_version = GetSdkVersion(); + m_minor_os_version = 0; + m_update_os_version = 0; + return m_major_os_version != 0; +} + +const char* +PlatformAndroid::GetLibdlFunctionDeclarations() const +{ + return R"( + extern "C" void* dlopen(const char*, int) asm("__dl_dlopen"); + extern "C" void* dlsym(void*, const char*) asm("__dl_dlsym"); + extern "C" int dlclose(void*) asm("__dl_dlclose"); + extern "C" char* dlerror(void) asm("__dl_dlerror"); + )"; +} diff --git a/source/Plugins/Platform/Android/PlatformAndroid.h b/source/Plugins/Platform/Android/PlatformAndroid.h new file mode 100644 index 0000000000000..119d0a0bdf04d --- /dev/null +++ b/source/Plugins/Platform/Android/PlatformAndroid.h @@ -0,0 +1,114 @@ +//===-- PlatformAndroid.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformAndroid_h_ +#define liblldb_PlatformAndroid_h_ + +// C Includes +// C++ Includes +#include <string> + +// Other libraries and framework includes +// Project includes +#include "Plugins/Platform/Linux/PlatformLinux.h" + +namespace lldb_private { +namespace platform_android { + + class PlatformAndroid : public platform_linux::PlatformLinux + { + public: + PlatformAndroid(bool is_host); + + ~PlatformAndroid() override; + + static void + Initialize (); + + static void + Terminate (); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const ArchSpec *arch); + + static ConstString + GetPluginNameStatic (bool is_host); + + static const char * + GetPluginDescriptionStatic (bool is_host); + + ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + + Error + ConnectRemote (Args& args) override; + + Error + GetFile (const FileSpec& source, + const FileSpec& destination) override; + + Error + PutFile (const FileSpec& source, + const FileSpec& destination, + uint32_t uid = UINT32_MAX, + uint32_t gid = UINT32_MAX) override; + + uint32_t + GetSdkVersion(); + + bool + GetRemoteOSVersion() override; + + Error + DisconnectRemote () override; + + uint32_t + GetDefaultMemoryCacheLineSize() override; + + protected: + const char * + GetCacheHostname () override; + + Error + DownloadModuleSlice (const FileSpec &src_file_spec, + const uint64_t src_offset, + const uint64_t src_size, + const FileSpec &dst_file_spec) override; + + Error + DownloadSymbolFile (const lldb::ModuleSP& module_sp, + const FileSpec& dst_file_spec) override; + + const char* + GetLibdlFunctionDeclarations() const override; + + private: + std::string m_device_id; + uint32_t m_sdk_version; + + DISALLOW_COPY_AND_ASSIGN (PlatformAndroid); + }; + +} // namespace platofor_android +} // namespace lldb_private + +#endif // liblldb_PlatformAndroid_h_ diff --git a/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp b/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp new file mode 100644 index 0000000000000..3d91dd6b7a32f --- /dev/null +++ b/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp @@ -0,0 +1,275 @@ +//===-- PlatformAndroidRemoteGDBServer.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Other libraries and framework includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Host/common/TCPSocket.h" +#include "PlatformAndroidRemoteGDBServer.h" +#include "Utility/UriParser.h" + +#include <sstream> + +using namespace lldb; +using namespace lldb_private; +using namespace platform_android; + +static const lldb::pid_t g_remote_platform_pid = 0; // Alias for the process id of lldb-platform + +static Error +ForwardPortWithAdb (const uint16_t local_port, + const uint16_t remote_port, + const char* remote_socket_name, + const llvm::Optional<AdbClient::UnixSocketNamespace>& socket_namespace, + std::string& device_id) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + + AdbClient adb; + auto error = AdbClient::CreateByDeviceID(device_id, adb); + if (error.Fail ()) + return error; + + device_id = adb.GetDeviceID(); + if (log) + log->Printf("Connected to Android device \"%s\"", device_id.c_str ()); + + if (remote_port != 0) + { + if (log) + log->Printf("Forwarding remote TCP port %d to local TCP port %d", remote_port, local_port); + return adb.SetPortForwarding(local_port, remote_port); + } + + if (log) + log->Printf("Forwarding remote socket \"%s\" to local TCP port %d", remote_socket_name, local_port); + + if (!socket_namespace) + return Error("Invalid socket namespace"); + + return adb.SetPortForwarding(local_port, remote_socket_name, *socket_namespace); +} + +static Error +DeleteForwardPortWithAdb (uint16_t local_port, const std::string& device_id) +{ + AdbClient adb (device_id); + return adb.DeletePortForwarding (local_port); +} + +static Error +FindUnusedPort (uint16_t& port) +{ + Error error; + std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(false, error)); + if (error.Fail()) + return error; + + error = tcp_socket->Listen("127.0.0.1:0", 1); + if (error.Success()) + port = tcp_socket->GetLocalPortNumber(); + + return error; +} + +PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer () +{ +} + +PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer () +{ + for (const auto& it : m_port_forwards) + DeleteForwardPortWithAdb(it.second, m_device_id); +} + +bool +PlatformAndroidRemoteGDBServer::LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url) +{ + uint16_t remote_port = 0; + std::string socket_name; + if (!m_gdb_client.LaunchGDBServer ("127.0.0.1", pid, remote_port, socket_name)) + return false; + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); + + auto error = MakeConnectURL (pid, + remote_port, + socket_name.c_str (), + connect_url); + if (error.Success() && log) + log->Printf("gdbserver connect URL: %s", connect_url.c_str()); + + return error.Success(); +} + +bool +PlatformAndroidRemoteGDBServer::KillSpawnedProcess (lldb::pid_t pid) +{ + DeleteForwardPort (pid); + return m_gdb_client.KillSpawnedProcess (pid); +} + +Error +PlatformAndroidRemoteGDBServer::ConnectRemote (Args& args) +{ + m_device_id.clear(); + + if (args.GetArgumentCount() != 1) + return Error("\"platform connect\" takes a single argument: <connect-url>"); + + int remote_port; + std::string scheme, host, path; + const char *url = args.GetArgumentAtIndex (0); + if (!url) + return Error("URL is null."); + if (!UriParser::Parse (url, scheme, host, remote_port, path)) + return Error("Invalid URL: %s", url); + if (host != "localhost") + m_device_id = host; + + m_socket_namespace.reset(); + if (scheme == ConnectionFileDescriptor::UNIX_CONNECT_SCHEME) + m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem; + else if (scheme == ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME) + m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract; + + std::string connect_url; + auto error = MakeConnectURL (g_remote_platform_pid, + (remote_port < 0) ? 0 : remote_port, + path.c_str (), + connect_url); + + if (error.Fail ()) + return error; + + args.ReplaceArgumentAtIndex (0, connect_url.c_str ()); + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("Rewritten platform connect URL: %s", connect_url.c_str()); + + error = PlatformRemoteGDBServer::ConnectRemote(args); + if (error.Fail ()) + DeleteForwardPort (g_remote_platform_pid); + + return error; +} + +Error +PlatformAndroidRemoteGDBServer::DisconnectRemote () +{ + DeleteForwardPort (g_remote_platform_pid); + return PlatformRemoteGDBServer::DisconnectRemote (); +} + +void +PlatformAndroidRemoteGDBServer::DeleteForwardPort (lldb::pid_t pid) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); + + auto it = m_port_forwards.find(pid); + if (it == m_port_forwards.end()) + return; + + const auto port = it->second; + const auto error = DeleteForwardPortWithAdb(port, m_device_id); + if (error.Fail()) { + if (log) + log->Printf("Failed to delete port forwarding (pid=%" PRIu64 ", port=%d, device=%s): %s", + pid, port, m_device_id.c_str(), error.AsCString()); + } + m_port_forwards.erase(it); +} + +Error +PlatformAndroidRemoteGDBServer::MakeConnectURL(const lldb::pid_t pid, + const uint16_t remote_port, + const char* remote_socket_name, + std::string& connect_url) +{ + static const int kAttempsNum = 5; + + Error error; + // There is a race possibility that somebody will occupy + // a port while we're in between FindUnusedPort and ForwardPortWithAdb - + // adding the loop to mitigate such problem. + for (auto i = 0; i < kAttempsNum; ++i) + { + uint16_t local_port = 0; + error = FindUnusedPort(local_port); + if (error.Fail()) + return error; + + error = ForwardPortWithAdb(local_port, + remote_port, + remote_socket_name, + m_socket_namespace, + m_device_id); + if (error.Success()) + { + m_port_forwards[pid] = local_port; + std::ostringstream url_str; + url_str << "connect://localhost:" << local_port; + connect_url = url_str.str(); + break; + } + } + + return error; +} + +lldb::ProcessSP +PlatformAndroidRemoteGDBServer::ConnectProcess(const char* connect_url, + const char* plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) +{ + // We don't have the pid of the remote gdbserver when it isn't started by us but we still want + // to store the list of port forwards we set up in our port forward map. Generate a fake pid for + // these cases what won't collide with any other valid pid on android. + static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL; + + int remote_port; + std::string scheme, host, path; + if (!UriParser::Parse(connect_url, scheme, host, remote_port, path)) + { + error.SetErrorStringWithFormat("Invalid URL: %s", connect_url); + return nullptr; + } + + std::string new_connect_url; + error = MakeConnectURL(s_remote_gdbserver_fake_pid--, + (remote_port < 0) ? 0 : remote_port, + path.c_str(), + new_connect_url); + if (error.Fail()) + return nullptr; + + return PlatformRemoteGDBServer::ConnectProcess(new_connect_url.c_str(), + plugin_name, + debugger, + target, + error); +} + +size_t +PlatformAndroidRemoteGDBServer::ConnectToWaitingProcesses(Debugger& debugger, Error& error) +{ + std::vector<std::string> connection_urls; + GetPendingGdbServerList(connection_urls); + + for (size_t i = 0; i < connection_urls.size(); ++i) + { + ConnectProcess(connection_urls[i].c_str(), nullptr, debugger, nullptr, error); + if (error.Fail()) + return i; // We already connected to i process succsessfully + } + return connection_urls.size(); +} diff --git a/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h b/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h new file mode 100644 index 0000000000000..3d2653812dedc --- /dev/null +++ b/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h @@ -0,0 +1,79 @@ +//===-- PlatformAndroidRemoteGDBServer.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformAndroidRemoteGDBServer_h_ +#define liblldb_PlatformAndroidRemoteGDBServer_h_ + +// C Includes +// C++ Includes +#include <map> +#include <utility> + +// Other libraries and framework includes +// Project includes +#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" + +#include "llvm/ADT/Optional.h" + +#include "AdbClient.h" + +namespace lldb_private { +namespace platform_android { + +class PlatformAndroidRemoteGDBServer : public platform_gdb_server::PlatformRemoteGDBServer +{ +public: + PlatformAndroidRemoteGDBServer(); + + ~PlatformAndroidRemoteGDBServer() override; + + Error + ConnectRemote (Args& args) override; + + Error + DisconnectRemote () override; + + lldb::ProcessSP + ConnectProcess (const char* connect_url, + const char* plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) override; + + size_t + ConnectToWaitingProcesses(lldb_private::Debugger& debugger, lldb_private::Error& error) override; + +protected: + std::string m_device_id; + std::map<lldb::pid_t, uint16_t> m_port_forwards; + llvm::Optional<AdbClient::UnixSocketNamespace> m_socket_namespace; + + bool + LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url) override; + + bool + KillSpawnedProcess (lldb::pid_t pid) override; + + void + DeleteForwardPort (lldb::pid_t pid); + + Error + MakeConnectURL(const lldb::pid_t pid, + const uint16_t remote_port, + const char* remote_socket_name, + std::string& connect_url); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformAndroidRemoteGDBServer); +}; + +} // namespace platform_android +} // namespace lldb_private + +#endif // liblldb_PlatformAndroidRemoteGDBServer_h_ |