labath created this revision.
labath added a reviewer: clayborg.
labath added a subscriber: lldb-commits.
Herald added subscribers: danalbert, tberghammer.

This adds the jModulesInfo packet, which is the equivalent of qModulesInfo, but 
it enables us to
query multiple modules at once. This makes a significant speed improvement in 
case the
application has many (over a hundred) modules, and the communication link has a 
non-negligible
latency. This functionality is accessed by 
ProcessGdbRemote::PrefetchModuleSpecs(), which does
the caching. GetModuleSpecs() is modified to first consult the cache before 
asking the remote
stub. PrefetchModuleSpecs is currently only called from POSIX-DYLD dynamic 
loader plugin, after
it reads the list of modules from the inferior memory, but other uses are 
possible.

This decreases the attach time to an android application by about 40%.

https://reviews.llvm.org/D24236

Files:
  docs/lldb-gdb-remote.txt
  include/lldb/Target/Process.h
  packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteModuleInfo.py
  source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
  source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
  source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
  source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
  source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
  source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
  source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
  source/Utility/StringExtractorGDBRemote.cpp
  source/Utility/StringExtractorGDBRemote.h
  unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp

Index: unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
===================================================================
--- unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
+++ unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
@@ -19,6 +19,7 @@
 
 #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h"
 #include "lldb/Core/DataBuffer.h"
+#include "lldb/Core/ModuleSpec.h"
 
 #include "llvm/ADT/ArrayRef.h"
 
@@ -182,3 +183,75 @@
     HandlePacket(server, "QSyncThreadState:0047;", "OK");
     ASSERT_TRUE(async_result.get());
 }
+
+TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo)
+{
+    TestClient client;
+    MockServer server;
+    Connect(client, server);
+    if (HasFailure())
+        return;
+
+    llvm::Triple triple("i386-pc-linux");
+
+    // Empty list of module specs should not send any packets
+    ASSERT_TRUE(client.GetModulesInfo({}, triple).empty());
+
+    FileSpec file_specs[] = { FileSpec("/foo/bar.so", false), FileSpec("/foo/baz.so", false) };
+    std::future<std::vector<ModuleSpec>> async_result =
+        std::async(std::launch::async, [&] { return client.GetModulesInfo(file_specs, triple); });
+    HandlePacket(server, "jModulesInfo:["
+                         R"({"file":"/foo/bar.so","triple":"i386-pc-linux"},)"
+                         R"({"file":"/foo/baz.so","triple":"i386-pc-linux"}])",
+                 R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
+                 R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])");
+
+    std::vector<ModuleSpec> result = async_result.get();
+    ASSERT_EQ(1u, result.size());
+    EXPECT_EQ("/foo/bar.so", result[0].GetFileSpec().GetPath());
+    EXPECT_EQ(triple, result[0].GetArchitecture().GetTriple());
+    EXPECT_EQ(UUID("@ABCDEFGHIJKLMNO", 16), result[0].GetUUID());
+    EXPECT_EQ(0u, result[0].GetObjectOffset());
+    EXPECT_EQ(1234u, result[0].GetObjectSize());
+}
+
+TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfoInvalidResponse)
+{
+    TestClient client;
+    MockServer server;
+    Connect(client, server);
+    if (HasFailure())
+        return;
+
+    llvm::Triple triple("i386-pc-linux");
+    FileSpec file_spec("/foo/bar.so", false);
+
+    const char *invalid_responses[] = {
+        "OK", "E47", "[]", "[{}]]",
+        // no UUID
+        R"([{"triple":"i386-pc-linux",)"
+        R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}])",
+        // no triple
+        R"([{"uuid":"404142434445464748494a4b4c4d4e4f",)"
+        R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}])",
+        // no file_path
+        R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
+        R"("file_offset":0,"file_size":1234}])", 
+        // no file_offset
+        R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
+        R"("file_path":"/foo/bar.so","file_size":1234}])",
+        // no file_size
+        R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
+        R"("file_path":"/foo/bar.so","file_offset":0}])",
+    };
+
+    for(const char *response: invalid_responses)
+    {
+        std::future<std::vector<ModuleSpec>> async_result =
+            std::async(std::launch::async, [&] { return client.GetModulesInfo(file_spec, triple); });
+        HandlePacket(server, R"(jModulesInfo:[{"file":"/foo/bar.so","triple":"i386-pc-linux"}])",
+                     response);
+
+        ASSERT_TRUE(async_result.get().empty());
+    }
+}
Index: source/Utility/StringExtractorGDBRemote.h
===================================================================
--- source/Utility/StringExtractorGDBRemote.h
+++ source/Utility/StringExtractorGDBRemote.h
@@ -146,6 +146,7 @@
         eServerPacketType_qXfer_auxv_read,
 
         eServerPacketType_jSignalsInfo,
+        eServerPacketType_jModulesInfo,
 
         eServerPacketType_vAttach,
         eServerPacketType_vAttachWait,
Index: source/Utility/StringExtractorGDBRemote.cpp
===================================================================
--- source/Utility/StringExtractorGDBRemote.cpp
+++ source/Utility/StringExtractorGDBRemote.cpp
@@ -223,6 +223,7 @@
         break;
 
     case 'j':
+        if (PACKET_STARTS_WITH("jModulesInfo:"))                return eServerPacketType_jModulesInfo;
         if (PACKET_MATCHES("jSignalsInfo"))                     return eServerPacketType_jSignalsInfo;
         if (PACKET_MATCHES("jThreadsInfo"))                     return eServerPacketType_jThreadsInfo;
         break;
Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
===================================================================
--- source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -28,6 +28,7 @@
 #include "lldb/Core/StringList.h"
 #include "lldb/Core/StructuredData.h"
 #include "lldb/Core/ThreadSafeValue.h"
+#include "lldb/Core/ModuleSpec.h"
 #include "lldb/Core/LoadedModuleInfoList.h"
 #include "lldb/Host/HostThread.h"
 #include "lldb/lldb-private-forward.h"
@@ -38,6 +39,8 @@
 #include "GDBRemoteCommunicationClient.h"
 #include "GDBRemoteRegisterContext.h"
 
+#include "llvm/ADT/DenseMap.h"
+
 namespace lldb_private {
 namespace process_gdb_remote {
 
@@ -241,6 +244,9 @@
                   const ArchSpec& arch,
                   ModuleSpec &module_spec) override;
 
+    void
+    PrefetchModuleSpecs(llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple) override;
+
     bool
     GetHostOSVersion(uint32_t &major,
                      uint32_t &minor,
@@ -514,6 +520,42 @@
     HandleAsyncStructuredData(const StructuredData::ObjectSP
                               &object_sp) override;
 
+
+    using ModuleCacheKey = std::pair<std::string, std::string>;
+    // KeyInfo for the cached module spec DenseMap.
+    // The invariant is that all real keys will have the file and architecture set.
+    // The empty key has an empty file and an empty arch.
+    // The tombstone key has an invalid arch and an empty file.
+    // The comparison and hash functions take the file name and architecture triple into account.
+    struct ModuleCacheInfo
+    {
+        static ModuleCacheKey
+        getEmptyKey()
+        {
+            return ModuleCacheKey();
+        }
+
+        static ModuleCacheKey
+        getTombstoneKey()
+        {
+            return ModuleCacheKey("", "T");
+        }
+
+        static unsigned
+        getHashValue(const ModuleCacheKey &key)
+        {
+            return llvm::hash_combine(key.first, key.second);
+        }
+
+        static bool
+        isEqual(const ModuleCacheKey &LHS, const ModuleCacheKey &RHS)
+        {
+            return LHS == RHS;
+        }
+    };
+
+    llvm::DenseMap<ModuleCacheKey, ModuleSpec, ModuleCacheInfo> m_cached_module_specs;
+
     DISALLOW_COPY_AND_ASSIGN (ProcessGDBRemote);
 };
 
Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===================================================================
--- source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -4397,6 +4397,14 @@
 {
     Log *log = GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PLATFORM);
 
+    const ModuleCacheKey key(module_file_spec.GetPath(), arch.GetTriple().getTriple());
+    auto cached = m_cached_module_specs.find(key);
+    if (cached != m_cached_module_specs.end())
+    {
+        module_spec = cached->second;
+        return bool(module_spec);
+    }
+
     if (!m_gdb_comm.GetModuleInfo (module_file_spec, arch, module_spec))
     {
         if (log)
@@ -4415,9 +4423,20 @@
                      arch.GetTriple ().getTriple ().c_str (), stream.GetString ().c_str ());
     }
 
+    m_cached_module_specs[key] = module_spec;
     return true;
 }
 
+void
+ProcessGDBRemote::PrefetchModuleSpecs(llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple)
+{
+    for (const FileSpec &spec : module_file_specs)
+        m_cached_module_specs[{spec.GetPath(), triple.getTriple()}] = ModuleSpec();
+    std::vector<ModuleSpec> module_specs = m_gdb_comm.GetModulesInfo(module_file_specs, triple);
+    for (const ModuleSpec &spec : module_specs)
+        m_cached_module_specs[{spec.GetFileSpec().GetPath(), triple.getTriple()}] = spec;
+}
+
 bool
 ProcessGDBRemote::GetHostOSVersion(uint32_t &major,
                                    uint32_t &minor,
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
@@ -109,6 +109,9 @@
     Handle_qModuleInfo (StringExtractorGDBRemote &packet);
 
     PacketResult
+    Handle_jModulesInfo(StringExtractorGDBRemote &packet);
+
+    PacketResult
     Handle_qPlatform_shell (StringExtractorGDBRemote &packet);
 
     PacketResult
@@ -192,6 +195,12 @@
 
     virtual FileSpec
     FindModuleFile (const std::string& module_path, const ArchSpec& arch);
+
+
+private:
+    ModuleSpec
+    GetModuleInfo(const std::string &module_path, const std::string &triple);
+
 };
 
 } // namespace process_gdb_remote
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
@@ -39,6 +39,7 @@
 #include "lldb/Target/FileAction.h"
 #include "lldb/Target/Platform.h"
 #include "lldb/Target/Process.h"
+#include "lldb/Utility/JSON.h"
 
 // Project includes
 #include "ProcessGDBRemoteLog.h"
@@ -94,6 +95,8 @@
                                   &GDBRemoteCommunicationServerCommon::Handle_qEcho);
     RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qModuleInfo,
                                   &GDBRemoteCommunicationServerCommon::Handle_qModuleInfo);
+    RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_jModulesInfo,
+                                  &GDBRemoteCommunicationServerCommon::Handle_jModulesInfo);
     RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qPlatform_chmod,
                                   &GDBRemoteCommunicationServerCommon::Handle_qPlatform_chmod);
     RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qPlatform_mkdir,
@@ -1125,20 +1128,11 @@
 
     std::string triple;
     packet.GetHexByteString(triple);
-    ArchSpec arch(triple.c_str());
-
-    const FileSpec req_module_path_spec(module_path.c_str(), true);
-    const FileSpec module_path_spec = FindModuleFile(req_module_path_spec.GetPath(), arch);
-    const ModuleSpec module_spec(module_path_spec, arch);
 
-    ModuleSpecList module_specs;
-    if (!ObjectFile::GetModuleSpecifications(module_path_spec, 0, 0, module_specs))
+    ModuleSpec matched_module_spec = GetModuleInfo(module_path, triple);
+    if (!matched_module_spec.GetFileSpec())
         return SendErrorResponse (3);
 
-    ModuleSpec matched_module_spec;
-    if (!module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec))
-        return SendErrorResponse (4);
-
     const auto file_offset = matched_module_spec.GetObjectOffset();
     const auto file_size = matched_module_spec.GetObjectSize();
     const auto uuid_str = matched_module_spec.GetUUID().GetAsString("");
@@ -1165,7 +1159,7 @@
     response.PutChar(';');
 
     response.PutCString("file_path:");
-    response.PutCStringAsRawHex8(module_path_spec.GetCString());
+    response.PutCStringAsRawHex8(matched_module_spec.GetFileSpec().GetCString());
     response.PutChar(';');
     response.PutCString("file_offset:");
     response.PutHex64(file_offset);
@@ -1177,6 +1171,57 @@
     return SendPacketNoLock(response.GetString());
 }
 
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_jModulesInfo(StringExtractorGDBRemote &packet)
+{
+    packet.SetFilePos(::strlen ("jModulesInfo:"));
+
+    StructuredData::ObjectSP object_sp = StructuredData::ParseJSON (packet.Peek());
+    if (!object_sp)
+        return SendErrorResponse(1);
+
+    StructuredData::Array *packet_array = object_sp->GetAsArray();
+    if (!packet_array)
+        return SendErrorResponse(2);
+
+    JSONArray::SP response_array_sp = std::make_shared<JSONArray>();
+    for (size_t i = 0; i < packet_array->GetSize(); ++i)
+    {
+        StructuredData::Dictionary *query = packet_array->GetItemAtIndex(i)->GetAsDictionary();
+        if (!query)
+            continue;
+        std::string file, triple;
+        if (!query->GetValueForKeyAsString("file", file) || !query->GetValueForKeyAsString("triple", triple))
+            continue;
+
+        ModuleSpec matched_module_spec = GetModuleInfo(file, triple);
+        if (!matched_module_spec.GetFileSpec())
+            continue;
+
+        const auto file_offset = matched_module_spec.GetObjectOffset();
+        const auto file_size = matched_module_spec.GetObjectSize();
+        const auto uuid_str = matched_module_spec.GetUUID().GetAsString("");
+
+        if (uuid_str.empty())
+            continue;
+
+        JSONObject::SP response = std::make_shared<JSONObject>();
+        response_array_sp->AppendObject(response);
+        response->SetObject("uuid", std::make_shared<JSONString>(uuid_str));
+        response->SetObject(
+            "triple", std::make_shared<JSONString>(matched_module_spec.GetArchitecture().GetTriple().getTriple()));
+        response->SetObject("file_path", std::make_shared<JSONString>(matched_module_spec.GetFileSpec().GetPath()));
+        response->SetObject("file_offset", std::make_shared<JSONNumber>(file_offset));
+        response->SetObject("file_size", std::make_shared<JSONNumber>(file_size));
+    }
+
+    StreamString response;
+    response_array_sp->Write(response);
+    StreamGDBRemote escaped_response;
+    escaped_response.PutEscapedBytes(response.GetData(), response.GetSize());
+    return SendPacketNoLock(escaped_response.GetString());
+}
+
 void
 GDBRemoteCommunicationServerCommon::CreateProcessInfoResponse (const ProcessInstanceInfo &proc_info,
                                                     StreamString &response)
@@ -1284,3 +1329,23 @@
     return FileSpec(module_path.c_str(), true);
 #endif
 }
+
+ModuleSpec
+GDBRemoteCommunicationServerCommon::GetModuleInfo(const std::string &module_path, const std::string &triple)
+{
+    ArchSpec arch(triple.c_str());
+
+    const FileSpec req_module_path_spec(module_path.c_str(), true);
+    const FileSpec module_path_spec = FindModuleFile(req_module_path_spec.GetPath(), arch);
+    const ModuleSpec module_spec(module_path_spec, arch);
+
+    ModuleSpecList module_specs;
+    if (!ObjectFile::GetModuleSpecifications(module_path_spec, 0, 0, module_specs))
+        return ModuleSpec();
+
+    ModuleSpec matched_module_spec;
+    if (!module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec))
+        return ModuleSpec();
+
+    return matched_module_spec;
+}
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -535,6 +535,9 @@
                    const ArchSpec& arch_spec,
                    ModuleSpec &module_spec);
 
+    std::vector<ModuleSpec>
+    GetModulesInfo(llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple);
+
     bool
     ReadExtFeature (const lldb_private::ConstString object,
                     const lldb_private::ConstString annex,
@@ -641,7 +644,8 @@
         m_supports_qSymbol:1,
         m_qSymbol_requests_done:1,
         m_supports_qModuleInfo:1,
-        m_supports_jThreadsInfo:1;
+        m_supports_jThreadsInfo:1,
+        m_supports_jModulesInfo:1;
     
     lldb::pid_t m_curr_pid;
     lldb::tid_t m_curr_tid;         // Current gdb remote protocol thread index for all other operations
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -33,6 +33,7 @@
 #include "lldb/Target/MemoryRegionInfo.h"
 #include "lldb/Target/UnixSignals.h"
 #include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/JSON.h"
 #include "lldb/Target/Target.h"
 
 // Project includes
@@ -42,6 +43,7 @@
 #include "lldb/Host/Config.h"
 
 #include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Optional.h"
 
 #if defined (HAVE_LIBCOMPRESSION)
 #include <compression.h>
@@ -104,6 +106,7 @@
       m_qSymbol_requests_done(false),
       m_supports_qModuleInfo(true),
       m_supports_jThreadsInfo(true),
+      m_supports_jModulesInfo(true),
       m_curr_pid(LLDB_INVALID_PROCESS_ID),
       m_curr_tid(LLDB_INVALID_THREAD_ID),
       m_curr_tid_run(LLDB_INVALID_THREAD_ID),
@@ -389,6 +392,7 @@
         m_qSupported_response.clear();
         m_supported_async_json_packets_is_valid = false;
         m_supported_async_json_packets_sp.reset();
+        m_supports_jModulesInfo = true;
     }
 
     // These flags should be reset when we first connect to a GDB server
@@ -3662,6 +3666,89 @@
     return true;
 }
 
+static llvm::Optional<ModuleSpec>
+ParseModuleSpec(StructuredData::Dictionary *dict)
+{
+    ModuleSpec result;
+    if (!dict)
+        return llvm::None;
+
+    std::string string;
+    uint64_t integer;
+
+    if (!dict->GetValueForKeyAsString("uuid", string))
+        return llvm::None;
+    result.GetUUID().SetFromCString(string.c_str(), string.size());
+
+    if (!dict->GetValueForKeyAsInteger("file_offset", integer))
+        return llvm::None;
+    result.SetObjectOffset(integer);
+
+    if (!dict->GetValueForKeyAsInteger("file_size", integer))
+        return llvm::None;
+    result.SetObjectSize(integer);
+
+    if (!dict->GetValueForKeyAsString("triple", string))
+        return llvm::None;
+    result.GetArchitecture().SetTriple(string.c_str());
+
+    if (!dict->GetValueForKeyAsString("file_path", string))
+        return llvm::None;
+    result.GetFileSpec() = FileSpec(string, false, result.GetArchitecture());
+
+    return result;
+}
+
+std::vector<ModuleSpec>
+GDBRemoteCommunicationClient::GetModulesInfo(llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple)
+{
+    if (!m_supports_jModulesInfo || module_file_specs.empty())
+        return {};
+
+    JSONArray::SP module_array_sp = std::make_shared<JSONArray>();
+    for (const FileSpec &module_file_spec: module_file_specs)
+    {
+        JSONObject::SP module_sp = std::make_shared<JSONObject>();
+        module_array_sp->AppendObject(module_sp);
+        module_sp->SetObject("file", std::make_shared<JSONString>(module_file_spec.GetPath()));
+        module_sp->SetObject("triple", std::make_shared<JSONString>(triple.getTriple()));
+    }
+    StreamString unescaped_payload;
+    unescaped_payload.PutCString("jModulesInfo:");
+    module_array_sp->Write(unescaped_payload);
+    StreamGDBRemote payload;
+    payload.PutEscapedBytes(unescaped_payload.GetData(), unescaped_payload.GetSize());
+
+    StringExtractorGDBRemote response;
+    if (SendPacketAndWaitForResponse(payload.GetString(), response, false) != PacketResult::Success ||
+        response.IsErrorResponse())
+        return {};
+
+    if (response.IsUnsupportedResponse ())
+    {
+        m_supports_jModulesInfo = false;
+        return {};
+    }
+
+    StructuredData::ObjectSP response_object_sp = StructuredData::ParseJSON(response.GetStringRef());
+    if (!response_object_sp)
+        return {};
+
+    StructuredData::Array *response_array = response_object_sp->GetAsArray();
+    if (! response_array)
+        return {};
+
+    std::vector<ModuleSpec> result;
+    for (size_t i = 0; i < response_array->GetSize(); ++i)
+    {
+        if (llvm::Optional<ModuleSpec> module_spec =
+            ParseModuleSpec(response_array->GetItemAtIndex(i)->GetAsDictionary()))
+            result.push_back(*module_spec);
+    }
+
+    return result;
+}
+
 // query the target remote for extended information using the qXfer packet
 //
 // example: object='features', annex='target.xml', out=<xml output>
Index: source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
===================================================================
--- source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
+++ source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
@@ -521,6 +521,12 @@
             module_list.Append(module_sp);
         }
     }
+    
+    std::vector<FileSpec> module_names;
+    for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I)
+        module_names.push_back(I->file_spec);
+    m_process->PrefetchModuleSpecs(module_names, m_process->GetTarget().GetArchitecture().GetTriple());
+
     for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I)
     {
         ModuleSP module_sp = LoadModuleAtAddress(I->file_spec, I->link_addr, I->base_addr, true);
Index: packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteModuleInfo.py
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteModuleInfo.py
@@ -0,0 +1,38 @@
+from __future__ import print_function
+
+
+
+import gdbremote_testcase
+import lldbgdbserverutils
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestGdbRemoteModuleInfo(gdbremote_testcase.GdbRemoteTestCaseBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    def module_info(self):
+        procs = self.prep_debug_monitor_and_inferior()
+        self.test_sequence.add_log_lines([
+            'read packet: $jModulesInfo:[{"file":"%s","triple":"%s"}]]#00'%(
+                lldbutil.append_to_process_working_directory("a.out"),
+                self.dbg.GetSelectedPlatform().GetTriple()),
+            {"direction":"send", "regex":r'^\$\[{(.*)}\]\]#[0-9A-Fa-f]{2}', "capture":{1:"spec"}},
+            ], True)
+
+        context = self.expect_gdbremote_sequence()
+        spec = context.get("spec")
+        self.assertRegexpMatches(spec, '"file_path":".*"')
+        self.assertRegexpMatches(spec, '"file_offset":\d+')
+        self.assertRegexpMatches(spec, '"file_size":\d+')
+        self.assertRegexpMatches(spec, '"triple":"\w*-\w*-.*"')
+        self.assertRegexpMatches(spec, '"uuid":"[A-Fa-f0-9]+"')
+
+    @llgs_test
+    def test_module_info(self):
+        self.init_llgs_test()
+        self.build()
+        self.set_inferior_startup_launch()
+        self.module_info()
+
Index: include/lldb/Target/Process.h
===================================================================
--- include/lldb/Target/Process.h
+++ include/lldb/Target/Process.h
@@ -50,6 +50,8 @@
 #include "lldb/Target/ThreadList.h"
 #include "lldb/Target/InstrumentationRuntime.h"
 
+#include "llvm/ADT/ArrayRef.h"
+
 namespace lldb_private {
 
 template <typename B, typename S>
@@ -3196,6 +3198,11 @@
     virtual bool
     GetModuleSpec(const FileSpec& module_file_spec, const ArchSpec& arch, ModuleSpec &module_spec);
 
+    virtual void
+    PrefetchModuleSpecs(llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple)
+    {
+    }
+
     //------------------------------------------------------------------
     /// Try to find the load address of a file.
     /// The load address is defined as the address of the first memory
Index: docs/lldb-gdb-remote.txt
===================================================================
--- docs/lldb-gdb-remote.txt
+++ docs/lldb-gdb-remote.txt
@@ -1054,6 +1054,24 @@
 //----------------------------------------------------------------------
 
 //----------------------------------------------------------------------
+// jModulesInfo:[{"file":"...",triple:"..."}, ...]
+//
+// BRIEF
+//  Get information for a list of modules by given module path and
+//  architecture.
+//
+// RESPONSE
+//  A JSON array of dictionaries containing the following keys: uuid,
+//  triple, file_path, file_offset, file_size. The meaning of the fields
+//  is the same as in the qModuleInfo packet.
+//
+// PRIORITY TO IMPLEMENT
+//  Optional. If not implemented, qModuleInfo packet will be used, which
+//  may be slower if the target contains a large number of modules and
+//  the communication link has a non-negligible latency.
+//----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
 // Stop reply packet extensions
 //
 // BRIEF
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to