Use ADB pull to download modules from android target.

http://reviews.llvm.org/D9816

llvm-svn: 237640
diff --git a/lldb/source/Plugins/Platform/Android/AdbClient.cpp b/lldb/source/Plugins/Platform/Android/AdbClient.cpp
index bcb58bd..2e574bc 100644
--- a/lldb/source/Plugins/Platform/Android/AdbClient.cpp
+++ b/lldb/source/Plugins/Platform/Android/AdbClient.cpp
@@ -8,14 +8,22 @@
 //===----------------------------------------------------------------------===//
 
 // 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 "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;
@@ -24,9 +32,10 @@
 
 namespace {
 
-const uint32_t kConnTimeout = 10000; // 10 ms
+const uint32_t kReadTimeout = 1000000; // 1 second
 const char * kOKAY = "OKAY";
 const char * kFAIL = "FAIL";
+const size_t kSyncPacketLen = 8;
 
 }  // namespace
 
@@ -63,7 +72,7 @@
 }
 
 void
-AdbClient::SetDeviceID (const std::string& device_id)
+AdbClient::SetDeviceID (const std::string &device_id)
 {
     m_device_id = device_id;
 }
@@ -96,10 +105,10 @@
     if (error.Fail ())
         return error;
 
-    std::string in_buffer;
+    std::vector<char> in_buffer;
     error = ReadMessage (in_buffer);
 
-    llvm::StringRef response (in_buffer);
+    llvm::StringRef response (&in_buffer[0], in_buffer.size ());
     llvm::SmallVector<llvm::StringRef, 4> devices;
     response.split (devices, "\n", -1, false);
 
@@ -136,11 +145,15 @@
 }
 
 Error
-AdbClient::SendMessage (const std::string &packet)
+AdbClient::SendMessage (const std::string &packet, const bool reconnect)
 {
-    auto error = Connect ();
-    if (error.Fail ())
-        return error;
+    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 ()));
@@ -164,26 +177,24 @@
 }
 
 Error
-AdbClient::ReadMessage (std::string &message)
+AdbClient::ReadMessage (std::vector<char> &message)
 {
     message.clear ();
 
     char buffer[5];
     buffer[4] = 0;
 
-    Error error;
-    ConnectionStatus status;
-
-    m_conn.Read (buffer, 4, kConnTimeout, status, &error);
+    auto error = ReadAllBytes (buffer, 4);
     if (error.Fail ())
         return error;
 
     unsigned int packet_len = 0;
     sscanf (buffer, "%x", &packet_len);
-    std::string result (packet_len, 0);
-    m_conn.Read (&result[0], packet_len, kConnTimeout, status, &error);
-    if (error.Success ())
-        result.swap (message);
+
+    message.resize (packet_len, 0);
+    error = ReadAllBytes (&message[0], packet_len);
+    if (error.Fail ())
+        message.clear ();
 
     return error;
 }
@@ -191,31 +202,169 @@
 Error
 AdbClient::ReadResponseStatus()
 {
-    char buffer[5];
+    char response_id[5];
 
     static const size_t packet_len = 4;
-    buffer[packet_len] = 0;
+    response_id[packet_len] = 0;
 
-    Error error;
-    ConnectionStatus status;
-
-    m_conn.Read (buffer, packet_len, kConnTimeout, status, &error);
+    auto error = ReadAllBytes (response_id, packet_len);
     if (error.Fail ())
         return error;
 
-    if (strncmp (buffer, kOKAY, packet_len) != 0)
+    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 char *remote_file, const char *local_file)
+{
+    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 ());
+
+    llvm::FileRemover local_file_remover (local_file);
+
+    std::ofstream dst (local_file, std::ios::out | std::ios::binary);
+    if (!dst.is_open ())
+        return Error ("Unable to open local file %s", local_file);
+
+    error = SendSyncRequest ("RECV", strlen(remote_file), remote_file);
+    if (error.Fail ())
+        return error;
+
+    std::vector<char> chunk;
+    bool eof = false;
+    while (!eof)
     {
-        if (strncmp (buffer, kFAIL, packet_len) == 0)
-        {
-            std::string error_message;
-            error = ReadMessage (error_message);
-            if (error.Fail ())
-                return error;
-            error.SetErrorString (error_message.c_str ());
-        }
-        else
-            error.SetErrorStringWithFormat ("\"%s\" expected from adb, received: \"%s\"", kOKAY, buffer);
+        error = PullFileChunk (chunk, eof);
+        if (error.Fail ())
+            return Error ("Failed to read file chunk: %s", error.AsCString ());
+        if (!eof)
+            dst.write (&chunk[0], chunk.size ());
     }
 
+    local_file_remover.releaseFile ();
+    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 == "DATA")
+    {
+        buffer.resize (data_len, 0);
+        error = ReadAllBytes (&buffer[0], data_len);
+        if (error.Fail ())
+            buffer.clear ();
+    }
+    else if (response_id == "DONE")
+        eof = true;
+    else
+        error = GetResponseError (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;
+
+    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;
 }
diff --git a/lldb/source/Plugins/Platform/Android/AdbClient.h b/lldb/source/Plugins/Platform/Android/AdbClient.h
index 235fc56..783e580 100644
--- a/lldb/source/Plugins/Platform/Android/AdbClient.h
+++ b/lldb/source/Plugins/Platform/Android/AdbClient.h
@@ -16,6 +16,7 @@
 
 #include <list>
 #include <string>
+#include <vector>
 
 // Other libraries and framework includes
 // Project includes
@@ -49,25 +50,49 @@
     Error
     DeletePortForwarding (const uint16_t port);
 
+    Error
+    PullFile (const char *remote_file, const char *local_file);
+
 private:
     Error
     Connect ();
 
     void
-    SetDeviceID (const std::string& device_id);
+    SetDeviceID (const std::string &device_id);
 
     Error
-    SendMessage (const std::string &packet);
+    SendMessage (const std::string &packet, const bool reconnect = true);
 
     Error
     SendDeviceMessage (const std::string &packet);
 
     Error
-    ReadMessage (std::string &message);
+    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
+    GetResponseError (const char *response_id);
 
     Error
     ReadResponseStatus ();
 
+    Error
+    SwitchDeviceTransport ();
+
+    Error
+    Sync ();
+
+    Error
+    PullFileChunk (std::vector<char> &buffer, bool &eof);
+
+    Error
+    ReadAllBytes (void *buffer, size_t size);
+
     std::string m_device_id;
     ConnectionFileDescriptor m_conn;
 };
diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
index 3bd628b..806714a 100644
--- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
+++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
@@ -212,3 +212,16 @@
 {
     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);
+
+    AdbClient adb (m_device_id);
+    return adb.PullFile (src_file_spec.GetPath (false).c_str (), dst_file_spec.GetPath ().c_str ());
+}
diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h
index 9b0a37b..02aa226 100644
--- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h
+++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h
@@ -68,6 +68,12 @@
         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;
+
     private:
         std::string m_device_id;
         DISALLOW_COPY_AND_ASSIGN (PlatformAndroid);