[Lldb-commits] [lldb] 3cd8d7b - [lldb] Remote disk file/directory completion for platform commands

Raphael Isemann via lldb-commits lldb-commits at lists.llvm.org
Mon Aug 24 08:56:29 PDT 2020


Author: Gongyu Deng
Date: 2020-08-24T17:55:54+02:00
New Revision: 3cd8d7b1727f06a701f41764c1109e5d321284b3

URL: https://github.com/llvm/llvm-project/commit/3cd8d7b1727f06a701f41764c1109e5d321284b3
DIFF: https://github.com/llvm/llvm-project/commit/3cd8d7b1727f06a701f41764c1109e5d321284b3.diff

LOG: [lldb] Remote disk file/directory completion for platform commands

1. Extended the gdb-remote communication related classes with disk file/directory
   completion functions;
2. Added two common completion functions RemoteDiskFiles and
   RemoteDiskDirectories based on the functions above;
3. Added completion for these commands:
   A. platform get-file <remote-file> <local-file>;
   B. platform put-file <local-file> <remote-file>;
   C. platform get-size <remote-file>;
   D. platform settings -w <remote-dir>;
   E. platform open file <remote-file>.
4. Added related tests for client and server;
5. Updated docs/lldb-platform-packets.txt.

Reviewed By: labath

Differential Revision: https://reviews.llvm.org/D85284

Added: 
    lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteDiskFileCompletion.py
    lldb/test/API/tools/lldb-server/TestGdbRemoteCompletion.py

Modified: 
    lldb/docs/lldb-platform-packets.txt
    lldb/include/lldb/Interpreter/CommandCompletions.h
    lldb/include/lldb/Target/Platform.h
    lldb/include/lldb/Utility/StringExtractorGDBRemote.h
    lldb/source/Commands/CommandCompletions.cpp
    lldb/source/Commands/CommandObjectPlatform.cpp
    lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
    lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h
    lldb/source/Utility/StringExtractorGDBRemote.cpp
    lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py

Removed: 
    


################################################################################
diff  --git a/lldb/docs/lldb-platform-packets.txt b/lldb/docs/lldb-platform-packets.txt
index 23d1cacc5f7e..8d3fed7ab341 100644
--- a/lldb/docs/lldb-platform-packets.txt
+++ b/lldb/docs/lldb-platform-packets.txt
@@ -237,6 +237,27 @@ incompatible with the flags that gdb specifies.
 //  Continues to return the results of the qfProcessInfo.  Once all matches
 //  have been sent, Exx is returned to indicate end of matches.
 
+//----------------------------------------------------------------------
+// qPathComplete
+//
+// BRIEF
+//   Get a list of matched disk files/directories by passing a boolean flag
+//   and a partial path.
+//
+// EXAMPLE
+//
+//   receive: qPathComplete:0,6d61696e
+//   send:    M6d61696e2e637070
+//   receive: qPathComplete:1,746573
+//   send:    M746573742f,74657374732f
+//
+//   If the first argument is zero, the result should contain all
+//   files (including directories) starting with the given path. If the
+//   argument is one, the result should contain only directories.
+//
+//   The result should be a comma-separated list of hex-encoded paths.
+//   Paths denoting a directory should end with a directory separator ('/' or '\').
+
 //----------------------------------------------------------------------
 // vFile:size:
 //

diff  --git a/lldb/include/lldb/Interpreter/CommandCompletions.h b/lldb/include/lldb/Interpreter/CommandCompletions.h
index 0392eaed62c5..b90e81cb95a8 100644
--- a/lldb/include/lldb/Interpreter/CommandCompletions.h
+++ b/lldb/include/lldb/Interpreter/CommandCompletions.h
@@ -47,10 +47,12 @@ class CommandCompletions {
     eBreakpointNameCompletion = (1u << 19),
     eProcessIDCompletion = (1u << 20),
     eProcessNameCompletion = (1u << 21),
+    eRemoteDiskFileCompletion = (1u << 22),
+    eRemoteDiskDirectoryCompletion = (1u << 23),
     // This item serves two purposes.  It is the last element in the enum, so
     // you can add custom enums starting from here in your Option class. Also
     // if you & in this bit the base code will not process the option.
-    eCustomCompletion = (1u << 22)
+    eCustomCompletion = (1u << 24)
   };
 
   static bool InvokeCommonCompletionCallbacks(
@@ -72,6 +74,14 @@ class CommandCompletions {
                               StringList &matches,
                               TildeExpressionResolver &Resolver);
 
+  static void RemoteDiskFiles(CommandInterpreter &interpreter,
+                              CompletionRequest &request,
+                              SearchFilter *searcher);
+
+  static void RemoteDiskDirectories(CommandInterpreter &interpreter,
+                                    CompletionRequest &request,
+                                    SearchFilter *searcher);
+
   static void SourceFiles(CommandInterpreter &interpreter,
                           CompletionRequest &request, SearchFilter *searcher);
 

diff  --git a/lldb/include/lldb/Target/Platform.h b/lldb/include/lldb/Target/Platform.h
index 6234b8244b3f..9335f73b37df 100644
--- a/lldb/include/lldb/Target/Platform.h
+++ b/lldb/include/lldb/Target/Platform.h
@@ -523,6 +523,9 @@ class Platform : public PluginInterface {
     return UINT64_MAX;
   }
 
+  virtual void AutoCompleteDiskFileOrDirectory(CompletionRequest &request,
+                                               bool only_dir) {}
+
   virtual uint64_t ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst,
                             uint64_t dst_len, Status &error) {
     error.SetErrorStringWithFormat(

diff  --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
index 715f3cb2541d..efb43767e739 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -76,6 +76,7 @@ class StringExtractorGDBRemote : public StringExtractor {
     eServerPacketType_QSetSTDERR,
     eServerPacketType_QSetWorkingDir,
     eServerPacketType_QStartNoAckMode,
+    eServerPacketType_qPathComplete,
     eServerPacketType_qPlatform_shell,
     eServerPacketType_qPlatform_mkdir,
     eServerPacketType_qPlatform_chmod,

diff  --git a/lldb/source/Commands/CommandCompletions.cpp b/lldb/source/Commands/CommandCompletions.cpp
index 9e74d8ad26cd..9221830b6460 100644
--- a/lldb/source/Commands/CommandCompletions.cpp
+++ b/lldb/source/Commands/CommandCompletions.cpp
@@ -73,6 +73,8 @@ bool CommandCompletions::InvokeCommonCompletionCallbacks(
       {eBreakpointNameCompletion, CommandCompletions::BreakpointNames},
       {eProcessIDCompletion, CommandCompletions::ProcessIDs},
       {eProcessNameCompletion, CommandCompletions::ProcessNames},
+      {eRemoteDiskFileCompletion, CommandCompletions::RemoteDiskFiles},
+      {eRemoteDiskDirectoryCompletion, CommandCompletions::RemoteDiskDirectories},
       {eNoCompletion, nullptr} // This one has to be last in the list.
   };
 
@@ -486,6 +488,24 @@ void CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name,
   DiskFilesOrDirectories(partial_file_name, true, matches, Resolver);
 }
 
+void CommandCompletions::RemoteDiskFiles(CommandInterpreter &interpreter,
+                                         CompletionRequest &request,
+                                         SearchFilter *searcher) {
+  lldb::PlatformSP platform_sp =
+      interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
+  if (platform_sp)
+    platform_sp->AutoCompleteDiskFileOrDirectory(request, false);
+}
+
+void CommandCompletions::RemoteDiskDirectories(CommandInterpreter &interpreter,
+                                               CompletionRequest &request,
+                                               SearchFilter *searcher) {
+  lldb::PlatformSP platform_sp =
+      interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
+  if (platform_sp)
+    platform_sp->AutoCompleteDiskFileOrDirectory(request, true);
+}
+
 void CommandCompletions::Modules(CommandInterpreter &interpreter,
                                  CompletionRequest &request,
                                  SearchFilter *searcher) {

diff  --git a/lldb/source/Commands/CommandObjectPlatform.cpp b/lldb/source/Commands/CommandObjectPlatform.cpp
index da113e5cb342..b5409e611f05 100644
--- a/lldb/source/Commands/CommandObjectPlatform.cpp
+++ b/lldb/source/Commands/CommandObjectPlatform.cpp
@@ -392,7 +392,8 @@ class CommandObjectPlatformSettings : public CommandObjectParsed {
                             "or for a platform by name.",
                             "platform settings", 0),
         m_options(),
-        m_option_working_dir(LLDB_OPT_SET_1, false, "working-dir", 'w', 0,
+        m_option_working_dir(LLDB_OPT_SET_1, false, "working-dir", 'w',
+                             CommandCompletions::eRemoteDiskDirectoryCompletion,
                              eArgTypePath,
                              "The working directory for the platform.") {
     m_options.Append(&m_option_working_dir, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
@@ -485,6 +486,15 @@ class CommandObjectPlatformFOpen : public CommandObjectParsed {
 
   ~CommandObjectPlatformFOpen() override = default;
 
+  void
+  HandleArgumentCompletion(CompletionRequest &request,
+                           OptionElementVector &opt_element_vector) override {
+    if (request.GetCursorIndex() == 0)
+      CommandCompletions::InvokeCommonCompletionCallbacks(
+          GetCommandInterpreter(),
+          CommandCompletions::eRemoteDiskFileCompletion, request, nullptr);
+  }
+
   bool DoExecute(Args &args, CommandReturnObject &result) override {
     PlatformSP platform_sp(
         GetDebugger().GetPlatformList().GetSelectedPlatform());
@@ -817,6 +827,19 @@ class CommandObjectPlatformGetFile : public CommandObjectParsed {
 
   ~CommandObjectPlatformGetFile() override = default;
 
+  void
+  HandleArgumentCompletion(CompletionRequest &request,
+                           OptionElementVector &opt_element_vector) override {
+    if (request.GetCursorIndex() == 0)
+      CommandCompletions::InvokeCommonCompletionCallbacks(
+          GetCommandInterpreter(),
+          CommandCompletions::eRemoteDiskFileCompletion, request, nullptr);
+    else if (request.GetCursorIndex() == 1)
+      CommandCompletions::InvokeCommonCompletionCallbacks(
+          GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+          request, nullptr);
+  }
+
   bool DoExecute(Args &args, CommandReturnObject &result) override {
     // If the number of arguments is incorrect, issue an error message.
     if (args.GetArgumentCount() != 2) {
@@ -882,6 +905,17 @@ class CommandObjectPlatformGetSize : public CommandObjectParsed {
 
   ~CommandObjectPlatformGetSize() override = default;
 
+  void
+  HandleArgumentCompletion(CompletionRequest &request,
+                           OptionElementVector &opt_element_vector) override {
+    if (request.GetCursorIndex() != 0)
+      return;
+
+    CommandCompletions::InvokeCommonCompletionCallbacks(
+        GetCommandInterpreter(), CommandCompletions::eRemoteDiskFileCompletion,
+        request, nullptr);
+  }
+
   bool DoExecute(Args &args, CommandReturnObject &result) override {
     // If the number of arguments is incorrect, issue an error message.
     if (args.GetArgumentCount() != 1) {
@@ -927,6 +961,19 @@ class CommandObjectPlatformPutFile : public CommandObjectParsed {
 
   ~CommandObjectPlatformPutFile() override = default;
 
+  void
+  HandleArgumentCompletion(CompletionRequest &request,
+                           OptionElementVector &opt_element_vector) override {
+    if (request.GetCursorIndex() == 0)
+      CommandCompletions::InvokeCommonCompletionCallbacks(
+          GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+          request, nullptr);
+    else if (request.GetCursorIndex() == 1)
+      CommandCompletions::InvokeCommonCompletionCallbacks(
+          GetCommandInterpreter(),
+          CommandCompletions::eRemoteDiskFileCompletion, request, nullptr);
+  }
+
   bool DoExecute(Args &args, CommandReturnObject &result) override {
     const char *src = args.GetArgumentAtIndex(0);
     const char *dst = args.GetArgumentAtIndex(1);

diff  --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
index 21bf7f4ac46d..e1eb15c3e8c9 100644
--- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
+++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
@@ -661,6 +661,11 @@ PlatformRemoteGDBServer::GetFileSize(const FileSpec &file_spec) {
   return m_gdb_client.GetFileSize(file_spec);
 }
 
+void PlatformRemoteGDBServer::AutoCompleteDiskFileOrDirectory(
+    CompletionRequest &request, bool only_dir) {
+  m_gdb_client.AutoCompleteDiskFileOrDirectory(request, only_dir);
+}
+
 uint64_t PlatformRemoteGDBServer::ReadFile(lldb::user_id_t fd, uint64_t offset,
                                            void *dst, uint64_t dst_len,
                                            Status &error) {

diff  --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
index 0602be1fa377..3562b2bb09df 100644
--- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
+++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
@@ -127,6 +127,9 @@ class PlatformRemoteGDBServer : public Platform, private UserIDResolver {
 
   lldb::user_id_t GetFileSize(const FileSpec &file_spec) override;
 
+  void AutoCompleteDiskFileOrDirectory(CompletionRequest &request,
+                                       bool only_dir) override;
+
   Status PutFile(const FileSpec &source, const FileSpec &destination,
                  uint32_t uid = UINT32_MAX, uint32_t gid = UINT32_MAX) override;
 

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index cf0daccc2d0c..2e6d174e4674 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -2982,6 +2982,31 @@ lldb::user_id_t GDBRemoteCommunicationClient::GetFileSize(
   return UINT64_MAX;
 }
 
+void GDBRemoteCommunicationClient::AutoCompleteDiskFileOrDirectory(
+    CompletionRequest &request, bool only_dir) {
+  lldb_private::StreamString stream;
+  stream.PutCString("qPathComplete:");
+  stream.PutHex32(only_dir ? 1 : 0);
+  stream.PutChar(',');
+  stream.PutStringAsRawHex8(request.GetCursorArgumentPrefix());
+  StringExtractorGDBRemote response;
+  if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+      PacketResult::Success) {
+    StreamString strm;
+    char ch = response.GetChar();
+    if (ch != 'M')
+      return;
+    while (response.Peek()) {
+      strm.Clear();
+      while ((ch = response.GetHexU8(0, false)) != '\0')
+        strm.PutChar(ch);
+      request.AddCompletion(strm.GetString());
+      if (response.GetChar() != ',')
+        break;
+    }
+  }
+}
+
 Status
 GDBRemoteCommunicationClient::GetFilePermissions(const FileSpec &file_spec,
                                                  uint32_t &file_permissions) {

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index 8df08cbde735..0159125a433b 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -375,6 +375,9 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
 
   lldb::user_id_t GetFileSize(const FileSpec &file_spec);
 
+  void AutoCompleteDiskFileOrDirectory(CompletionRequest &request,
+                                       bool only_dir);
+
   Status GetFilePermissions(const FileSpec &file_spec,
                             uint32_t &file_permissions);
 

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
index d14b79a03d17..7e94afb9ec68 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
@@ -26,12 +26,14 @@
 #include "lldb/Host/FileAction.h"
 #include "lldb/Host/Host.h"
 #include "lldb/Host/HostInfo.h"
+#include "lldb/Interpreter/CommandCompletions.h"
 #include "lldb/Target/Platform.h"
 #include "lldb/Target/UnixSignals.h"
 #include "lldb/Utility/GDBRemote.h"
 #include "lldb/Utility/Log.h"
 #include "lldb/Utility/StreamString.h"
 #include "lldb/Utility/StructuredData.h"
+#include "lldb/Utility/TildeExpressionResolver.h"
 #include "lldb/Utility/UriParser.h"
 
 #include "lldb/Utility/StringExtractorGDBRemote.h"
@@ -68,6 +70,9 @@ GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform(
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_qProcessInfo,
       &GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo);
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_qPathComplete,
+      &GDBRemoteCommunicationServerPlatform::Handle_qPathComplete);
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir,
       &GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir);
@@ -333,6 +338,38 @@ GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo(
   return SendPacketNoLock(response.GetString());
 }
 
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qPathComplete(
+    StringExtractorGDBRemote &packet) {
+  packet.SetFilePos(::strlen("qPathComplete:"));
+  const bool only_dir = (packet.GetHexMaxU32(false, 0) == 1);
+  if (packet.GetChar() != ',')
+    return SendErrorResponse(85);
+  std::string path;
+  packet.GetHexByteString(path);
+
+  StringList matches;
+  StandardTildeExpressionResolver resolver;
+  if (only_dir)
+    CommandCompletions::DiskDirectories(path, matches, resolver);
+  else
+    CommandCompletions::DiskFiles(path, matches, resolver);
+
+  StreamString response;
+  response.PutChar('M');
+  llvm::StringRef separator;
+  std::sort(matches.begin(), matches.end());
+  for (const auto &match : matches) {
+    response << separator;
+    separator = ",";
+    // encode result strings into hex bytes to avoid unexpected error caused by
+    // special characters like '$'.
+    response.PutStringAsRawHex8(match.c_str());
+  }
+
+  return SendPacketNoLock(response.GetString());
+}
+
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir(
     StringExtractorGDBRemote &packet) {

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h
index a8cacea78835..8b3122d1423e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h
@@ -81,6 +81,8 @@ class GDBRemoteCommunicationServerPlatform
 
   PacketResult Handle_qKillSpawnedProcess(StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_qPathComplete(StringExtractorGDBRemote &packet);
+
   PacketResult Handle_qProcessInfo(StringExtractorGDBRemote &packet);
 
   PacketResult Handle_qGetWorkingDir(StringExtractorGDBRemote &packet);

diff  --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp
index cfe7577e4863..2901500b29e3 100644
--- a/lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -233,6 +233,8 @@ StringExtractorGDBRemote::GetServerPacketType() const {
         return eServerPacketType_qPlatform_chmod;
       if (PACKET_MATCHES("qProcessInfo"))
         return eServerPacketType_qProcessInfo;
+      if (PACKET_STARTS_WITH("qPathComplete:"))
+        return eServerPacketType_qPathComplete;
       break;
 
     case 'Q':

diff  --git a/lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteDiskFileCompletion.py b/lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteDiskFileCompletion.py
new file mode 100644
index 000000000000..90f830c4a278
--- /dev/null
+++ b/lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteDiskFileCompletion.py
@@ -0,0 +1,29 @@
+from gdbclientutils import *
+
+class TestGDBRemoteDiskFileCompletion(GDBRemoteTestBase):
+
+    def test_autocomplete_request(self):
+        """Test remote disk completion on remote-gdb-server plugin"""
+
+        class Responder(MockGDBServerResponder):
+            def qPathComplete(self):
+                return "M{},{}".format(
+                    "test".encode().hex(),
+                    "123".encode().hex()
+                )
+
+        self.server.responder = Responder()
+
+        try:
+            self.runCmd("platform select remote-gdb-server")
+            self.runCmd("platform connect connect://localhost:%d" %
+                        self.server.port)
+            self.assertTrue(self.dbg.GetSelectedPlatform().IsConnected())
+
+            self.complete_from_to('platform get-size ', ['test', '123'])
+            self.complete_from_to('platform get-file ', ['test', '123'])
+            self.complete_from_to('platform put-file foo ', ['test', '123'])
+            self.complete_from_to('platform file open ', ['test', '123'])
+            self.complete_from_to('platform settings -w ', ['test', '123'])
+        finally:
+            self.dbg.GetSelectedPlatform().DisconnectRemote()

diff  --git a/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py b/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py
index feda8873d872..eb789e861d9c 100644
--- a/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py
+++ b/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py
@@ -178,6 +178,8 @@ def respond(self, packet):
             return self.qsProcessInfo()
         if packet.startswith("qfProcessInfo"):
             return self.qfProcessInfo(packet)
+        if packet.startswith("qPathComplete:"):
+            return self.qPathComplete()
 
         return self.other(packet)
 
@@ -282,6 +284,9 @@ def QListThreadsInStopReply(self):
     def qMemoryRegionInfo(self):
         return ""
 
+    def qPathComplete(self):
+        return ""
+
     """
     Raised when we receive a packet for which there is no default action.
     Override the responder class to implement behavior suitable for the test at

diff  --git a/lldb/test/API/tools/lldb-server/TestGdbRemoteCompletion.py b/lldb/test/API/tools/lldb-server/TestGdbRemoteCompletion.py
new file mode 100644
index 000000000000..94e628c811af
--- /dev/null
+++ b/lldb/test/API/tools/lldb-server/TestGdbRemoteCompletion.py
@@ -0,0 +1,63 @@
+import tempfile
+import gdbremote_testcase
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbgdbserverutils import *
+
+class GdbRemoteCompletionTestCase(gdbremote_testcase.GdbRemoteTestCaseBase):
+    mydir = TestBase.compute_mydir(__file__)
+
+    def init_lldb_server(self):
+        self.debug_monitor_exe = get_lldb_server_exe()
+        if not self.debug_monitor_exe:
+            self.skipTest("lldb-server exe not found")
+        port_file = tempfile.NamedTemporaryFile().name
+        commandline_args = [
+            "platform",
+            "--listen",
+            "*:0",
+            "--socket-file",
+            port_file
+        ]
+        server = self.spawnSubprocess(
+            get_lldb_server_exe(),
+            commandline_args,
+            install_remote=False)
+        self.assertIsNotNone(server)
+        self.stub_hostname = "localhost"
+        self.port = int(lldbutil.wait_for_file_on_target(self, port_file))
+        self.sock = self.create_socket()
+
+        self.add_no_ack_remote_stream()
+
+    def generate_hex_path(self, target):
+        return str(os.path.join(self.getBuildDir(), target)).encode().hex()
+
+    @skipIfDarwinEmbedded # <rdar://problem/34539270> lldb-server tests not updated to work on ios etc yet
+    @llgs_test
+    def test_autocomplete_path(self):
+        self.build()
+        self.init_lldb_server()
+
+        # Test file-included completion when flag is set to 0.
+        self.test_sequence.add_log_lines(
+            ["read packet: $qPathComplete:0,{}#00".format(
+                self.generate_hex_path("main")),
+             "send packet: $M{},{}#00".format(
+                self.generate_hex_path("main.d"),
+                self.generate_hex_path("main.o"))
+            ],
+            True)
+
+        # Test directory-only completion when flag is set to 1.
+        os.makedirs(os.path.join(self.getBuildDir(), "test"))
+        self.test_sequence.add_log_lines(
+            ["read packet: $qPathComplete:1,{}#00".format(
+                self.generate_hex_path("tes")),
+             "send packet: $M{}{}#00".format(
+                self.generate_hex_path("test"),
+                os.path.sep.encode().hex()) # "test/" or "test\".
+            ],
+            True)
+
+        self.expect_gdbremote_sequence()


        


More information about the lldb-commits mailing list