Index: lldb/trunk/docs/lldb-gdb-remote.txt =================================================================== --- lldb/trunk/docs/lldb-gdb-remote.txt +++ lldb/trunk/docs/lldb-gdb-remote.txt @@ -1054,6 +1054,28 @@ //---------------------------------------------------------------------- //---------------------------------------------------------------------- +// 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. The server signals the +// failure to retrieve the module info for a file by ommiting the +// corresponding array entry from the response. The server may also +// include entries the client did not ask for, if it has reason to +// the modules will be interesting to the client. +// +// 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 Index: lldb/trunk/include/lldb/Target/Process.h =================================================================== --- lldb/trunk/include/lldb/Target/Process.h +++ lldb/trunk/include/lldb/Target/Process.h @@ -50,6 +50,8 @@ #include "lldb/Target/ThreadList.h" #include "lldb/lldb-private.h" +#include "llvm/ADT/ArrayRef.h" + namespace lldb_private { template struct Range; @@ -2646,6 +2648,9 @@ virtual bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec); + virtual void PrefetchModuleSpecs(llvm::ArrayRef 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: lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteModuleInfo.py =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteModuleInfo.py +++ lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteModuleInfo.py @@ -0,0 +1,39 @@ +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: lldb/trunk/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp =================================================================== --- lldb/trunk/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ lldb/trunk/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -509,6 +509,13 @@ module_list.Append(module_sp); } } + + std::vector 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: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -26,6 +26,8 @@ #include "lldb/Core/StructuredData.h" #include "lldb/Target/Process.h" +#include "llvm/ADT/Optional.h" + namespace lldb_private { namespace process_gdb_remote { @@ -437,6 +439,10 @@ bool GetModuleInfo(const FileSpec &module_file_spec, const ArchSpec &arch_spec, ModuleSpec &module_spec); + llvm::Optional> + GetModulesInfo(llvm::ArrayRef module_file_specs, + const llvm::Triple &triple); + bool ReadExtFeature(const lldb_private::ConstString object, const lldb_private::ConstString annex, std::string &out, lldb_private::Error &err); @@ -527,7 +533,8 @@ m_supports_z2 : 1, m_supports_z3 : 1, m_supports_z4 : 1, m_supports_QEnvironment : 1, m_supports_QEnvironmentHexEncoded : 1, m_supports_qSymbol : 1, m_qSymbol_requests_done : 1, - m_supports_qModuleInfo : 1, m_supports_jThreadsInfo : 1; + m_supports_qModuleInfo : 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 Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -32,6 +32,7 @@ #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/JSON.h" #include "lldb/Utility/LLDBAssert.h" // Project includes @@ -93,8 +94,8 @@ m_supports_z4(true), m_supports_QEnvironment(true), m_supports_QEnvironmentHexEncoded(true), m_supports_qSymbol(true), m_qSymbol_requests_done(false), m_supports_qModuleInfo(true), - m_supports_jThreadsInfo(true), m_curr_pid(LLDB_INVALID_PROCESS_ID), - m_curr_tid(LLDB_INVALID_THREAD_ID), + 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), m_num_supported_hardware_watchpoints(0), m_host_arch(), m_process_arch(), m_os_version_major(UINT32_MAX), m_os_version_minor(UINT32_MAX), @@ -323,6 +324,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 @@ -3232,6 +3234,90 @@ return true; } +static llvm::Optional +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; +} + +llvm::Optional> +GDBRemoteCommunicationClient::GetModulesInfo( + llvm::ArrayRef module_file_specs, const llvm::Triple &triple) { + if (!m_supports_jModulesInfo) + return llvm::None; + + JSONArray::SP module_array_sp = std::make_shared(); + for (const FileSpec &module_file_spec : module_file_specs) { + JSONObject::SP module_sp = std::make_shared(); + module_array_sp->AppendObject(module_sp); + module_sp->SetObject( + "file", std::make_shared(module_file_spec.GetPath())); + module_sp->SetObject("triple", + std::make_shared(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 llvm::None; + + if (response.IsUnsupportedResponse()) { + m_supports_jModulesInfo = false; + return llvm::None; + } + + StructuredData::ObjectSP response_object_sp = + StructuredData::ParseJSON(response.GetStringRef()); + if (!response_object_sp) + return llvm::None; + + StructuredData::Array *response_array = response_object_sp->GetAsArray(); + if (!response_array) + return llvm::None; + + std::vector result; + for (size_t i = 0; i < response_array->GetSize(); ++i) { + if (llvm::Optional 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= Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h +++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h @@ -86,6 +86,8 @@ PacketResult Handle_qModuleInfo(StringExtractorGDBRemote &packet); + PacketResult Handle_jModulesInfo(StringExtractorGDBRemote &packet); + PacketResult Handle_qPlatform_shell(StringExtractorGDBRemote &packet); PacketResult Handle_qPlatform_mkdir(StringExtractorGDBRemote &packet); @@ -149,6 +151,10 @@ 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: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp +++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -38,6 +38,7 @@ #include "lldb/Target/FileAction.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" +#include "lldb/Utility/JSON.h" #include "llvm/ADT/Triple.h" // Project includes @@ -102,6 +103,9 @@ StringExtractorGDBRemote::eServerPacketType_qModuleInfo, &GDBRemoteCommunicationServerCommon::Handle_qModuleInfo); RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jModulesInfo, + &GDBRemoteCommunicationServerCommon::Handle_jModulesInfo); + RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qPlatform_chmod, &GDBRemoteCommunicationServerCommon::Handle_qPlatform_chmod); RegisterMemberFunctionHandler( @@ -1078,22 +1082,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(""); @@ -1119,7 +1112,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); @@ -1131,6 +1124,63 @@ 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(); + 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(); + response_array_sp->AppendObject(response); + response->SetObject("uuid", std::make_shared(uuid_str)); + response->SetObject( + "triple", + std::make_shared( + matched_module_spec.GetArchitecture().GetTriple().getTriple())); + response->SetObject("file_path", + std::make_shared( + matched_module_spec.GetFileSpec().GetPath())); + response->SetObject("file_offset", + std::make_shared(file_offset)); + response->SetObject("file_size", std::make_shared(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) { response.Printf( @@ -1230,3 +1280,24 @@ 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: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -25,6 +25,7 @@ #include "lldb/Core/ConstString.h" #include "lldb/Core/Error.h" #include "lldb/Core/LoadedModuleInfoList.h" +#include "lldb/Core/ModuleSpec.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/StringList.h" #include "lldb/Core/StructuredData.h" @@ -38,6 +39,8 @@ #include "GDBRemoteCommunicationClient.h" #include "GDBRemoteRegisterContext.h" +#include "llvm/ADT/DenseMap.h" + namespace lldb_private { namespace process_gdb_remote { @@ -194,6 +197,9 @@ bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec) override; + void PrefetchModuleSpecs(llvm::ArrayRef module_file_specs, + const llvm::Triple &triple) override; + bool GetHostOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) override; @@ -412,6 +418,31 @@ bool HandleAsyncStructuredData(const StructuredData::ObjectSP &object_sp) override; + using ModuleCacheKey = std::pair; + // 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 + m_cached_module_specs; + DISALLOW_COPY_AND_ASSIGN(ProcessGDBRemote); }; Index: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -4020,6 +4020,14 @@ ModuleSpec &module_spec) { 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) log->Printf("ProcessGDBRemote::%s - failed to get module info for %s:%s", @@ -4037,9 +4045,23 @@ stream.GetString().c_str()); } + m_cached_module_specs[key] = module_spec; return true; } +void ProcessGDBRemote::PrefetchModuleSpecs( + llvm::ArrayRef module_file_specs, const llvm::Triple &triple) { + auto module_specs = m_gdb_comm.GetModulesInfo(module_file_specs, triple); + if (module_specs) { + for (const FileSpec &spec : module_file_specs) + m_cached_module_specs[{spec.GetPath(), triple.getTriple()}] = + ModuleSpec(); + 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, uint32_t &update) { if (m_gdb_comm.GetOSVersion(major, minor, update)) Index: lldb/trunk/source/Utility/StringExtractorGDBRemote.h =================================================================== --- lldb/trunk/source/Utility/StringExtractorGDBRemote.h +++ lldb/trunk/source/Utility/StringExtractorGDBRemote.h @@ -128,6 +128,7 @@ eServerPacketType_qXfer_auxv_read, eServerPacketType_jSignalsInfo, + eServerPacketType_jModulesInfo, eServerPacketType_vAttach, eServerPacketType_vAttachWait, Index: lldb/trunk/source/Utility/StringExtractorGDBRemote.cpp =================================================================== --- lldb/trunk/source/Utility/StringExtractorGDBRemote.cpp +++ lldb/trunk/source/Utility/StringExtractorGDBRemote.cpp @@ -279,6 +279,8 @@ break; case 'j': + if (PACKET_STARTS_WITH("jModulesInfo:")) + return eServerPacketType_jModulesInfo; if (PACKET_MATCHES("jSignalsInfo")) return eServerPacketType_jSignalsInfo; if (PACKET_MATCHES("jThreadsInfo")) Index: lldb/trunk/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp =================================================================== --- lldb/trunk/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp +++ lldb/trunk/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" @@ -185,3 +186,76 @@ 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"); + + FileSpec file_specs[] = {FileSpec("/foo/bar.so", false), + FileSpec("/foo/baz.so", false)}; + std::future>> 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}]])"); + + auto result = async_result.get(); + ASSERT_TRUE(result.hasValue()); + ASSERT_EQ(1u, result->size()); + EXPECT_EQ("/foo/bar.so", result.getValue()[0].GetFileSpec().GetPath()); + EXPECT_EQ(triple, result.getValue()[0].GetArchitecture().GetTriple()); + EXPECT_EQ(UUID("@ABCDEFGHIJKLMNO", 16), result.getValue()[0].GetUUID()); + EXPECT_EQ(0u, result.getValue()[0].GetObjectOffset()); + EXPECT_EQ(1234u, result.getValue()[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>> 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_FALSE(async_result.get().hasValue()) << "response was: " << response; + } +}