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 000000000000..736447fd22d2 --- /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 000000000000..4ec411d1411d --- /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 000000000000..e831a33a4b6d --- /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 000000000000..aa186f924e66 --- /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 000000000000..e842884c046a --- /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 000000000000..119d0a0bdf04 --- /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 000000000000..3d91dd6b7a32 --- /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 000000000000..3d2653812ded --- /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_ | 
