mgorny created this revision.
mgorny added reviewers: labath, krytarowski, jasonmolenda, emaste, JDevlieghere.
mgorny requested review of this revision.

Implement a fallback to getting the file size via vFile:stat packet
when the remote server does not implement vFile:size.  This makes it
possible to query file sizes from remote gdbserver.

While at it, add a few tests for the 'platform get-size' command.


https://reviews.llvm.org/D107780

Files:
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
  lldb/test/API/functionalities/gdb_remote_client/TestGDBRemotePlatformFile.py

Index: lldb/test/API/functionalities/gdb_remote_client/TestGDBRemotePlatformFile.py
===================================================================
--- lldb/test/API/functionalities/gdb_remote_client/TestGDBRemotePlatformFile.py
+++ lldb/test/API/functionalities/gdb_remote_client/TestGDBRemotePlatformFile.py
@@ -77,3 +77,58 @@
                 ])
         finally:
             self.dbg.GetSelectedPlatform().DisconnectRemote()
+
+    def test_file_size(self):
+        """Test 'platform get-size'"""
+
+        class Responder(MockGDBServerResponder):
+            def vFile(self, packet):
+                return "F1000"
+
+        self.server.responder = Responder()
+
+        try:
+            self.runCmd("platform select remote-gdb-server")
+            self.runCmd("platform connect connect://" +
+                        self.server.get_connect_address())
+            self.assertTrue(self.dbg.GetSelectedPlatform().IsConnected())
+
+            self.match("platform get-size /some/file.txt",
+                       [r"File size of /some/file\.txt \(remote\): 4096"])
+            self.assertPacketLogContains([
+                "vFile:size:2f736f6d652f66696c652e747874",
+                ])
+        finally:
+            self.dbg.GetSelectedPlatform().DisconnectRemote()
+
+    def test_file_size_fallback(self):
+        """Test 'platform get-size fallback to vFile:fstat'"""
+
+        class Responder(MockGDBServerResponder):
+            def vFile(self, packet):
+                if packet.startswith("vFile:open:"):
+                    return "F5"
+                elif packet.startswith("vFile:fstat:"):
+                    return "F40;" + 28 * "\0" + "\0\0\0\0\0\1\2\3" + 28 * "\0"
+                if packet.startswith("vFile:close:"):
+                    return "F0"
+                return ""
+
+        self.server.responder = Responder()
+
+        try:
+            self.runCmd("platform select remote-gdb-server")
+            self.runCmd("platform connect connect://" +
+                        self.server.get_connect_address())
+            self.assertTrue(self.dbg.GetSelectedPlatform().IsConnected())
+
+            self.match("platform get-size /some/file.txt",
+                       [r"File size of /some/file\.txt \(remote\): 66051"])
+            self.assertPacketLogContains([
+                "vFile:size:2f736f6d652f66696c652e747874",
+                "vFile:open:2f736f6d652f66696c652e747874,00000000,00000000",
+                "vFile:fstat:5",
+                "vFile:close:5",
+                ])
+        finally:
+            self.dbg.GetSelectedPlatform().DisconnectRemote()
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -391,6 +391,8 @@
 
   bool CloseFile(lldb::user_id_t fd, Status &error);
 
+  llvm::Optional<GDBRemoteFStatData> FStat(lldb::user_id_t fd);
+
   lldb::user_id_t GetFileSize(const FileSpec &file_spec);
 
   void AutoCompleteDiskFileOrDirectory(CompletionRequest &request,
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -3035,6 +3035,46 @@
   return false;
 }
 
+llvm::Optional<GDBRemoteFStatData>
+GDBRemoteCommunicationClient::FStat(lldb::user_id_t fd) {
+  lldb_private::StreamString stream;
+  stream.Printf("vFile:fstat:%x", fd);
+  StringExtractorGDBRemote response;
+  if (SendPacketAndWaitForResponse(stream.GetString(), response) ==
+      PacketResult::Success) {
+    if (response.GetChar() != 'F')
+      return llvm::None;
+    int64_t size = response.GetS64(-1, 16);
+    if (size > 0 && response.GetChar() == ';') {
+      std::string buffer;
+      if (response.GetEscapedBinaryData(buffer)) {
+        DataExtractor extractor{buffer.data(), buffer.size(),
+                                lldb::eByteOrderBig, sizeof(void *)};
+        lldb::offset_t offset = 0;
+        GDBRemoteFStatData out;
+
+        out.gdb_st_dev = extractor.GetU32(&offset);
+        out.gdb_st_ino = extractor.GetU32(&offset);
+        out.gdb_st_mode = extractor.GetU32(&offset);
+        out.gdb_st_nlink = extractor.GetU32(&offset);
+        out.gdb_st_uid = extractor.GetU32(&offset);
+        out.gdb_st_gid = extractor.GetU32(&offset);
+        out.gdb_st_rdev = extractor.GetU32(&offset);
+        out.gdb_st_size = extractor.GetU64(&offset);
+        out.gdb_st_blksize = extractor.GetU64(&offset);
+        out.gdb_st_blocks = extractor.GetU64(&offset);
+        out.gdb_st_atime = extractor.GetU32(&offset);
+        out.gdb_st_mtime = extractor.GetU32(&offset);
+        out.gdb_st_ctime = extractor.GetU32(&offset);
+        assert(offset == 64);
+
+        return out;
+      }
+    }
+  }
+  return llvm::None;
+}
+
 // Extension of host I/O packets to get the file size.
 lldb::user_id_t GDBRemoteCommunicationClient::GetFileSize(
     const lldb_private::FileSpec &file_spec) {
@@ -3045,10 +3085,22 @@
   StringExtractorGDBRemote response;
   if (SendPacketAndWaitForResponse(stream.GetString(), response) ==
       PacketResult::Success) {
-    if (response.GetChar() != 'F')
-      return UINT64_MAX;
-    uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX);
-    return retcode;
+    if (!response.IsUnsupportedResponse()) {
+      if (response.GetChar() != 'F')
+        return UINT64_MAX;
+      uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX);
+      return retcode;
+    } else {
+      // fallback to fstat
+      Status error;
+      lldb::user_id_t fd = OpenFile(file_spec, File::eOpenOptionReadOnly, 0, error);
+      if (fd == -1)
+        return UINT64_MAX;
+      llvm::Optional<GDBRemoteFStatData> st = FStat(fd);
+      CloseFile(fd, error);
+      if (st)
+        return st->gdb_st_size;
+    }
   }
   return UINT64_MAX;
 }
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
@@ -51,6 +51,24 @@
   LZMA, // Lempel–Ziv–Markov chain algorithm
 };
 
+// Data included in the vFile:fstat packet.
+// https://sourceware.org/gdb/onlinedocs/gdb/struct-stat.html#struct-stat
+struct GDBRemoteFStatData {
+  uint32_t gdb_st_dev;
+  uint32_t gdb_st_ino;
+  uint32_t gdb_st_mode;
+  uint32_t gdb_st_nlink;
+  uint32_t gdb_st_uid;
+  uint32_t gdb_st_gid;
+  uint32_t gdb_st_rdev;
+  uint64_t gdb_st_size;
+  uint64_t gdb_st_blksize;
+  uint64_t gdb_st_blocks;
+  uint32_t gdb_st_atime;
+  uint32_t gdb_st_mtime;
+  uint32_t gdb_st_ctime;
+};
+
 class ProcessGDBRemote;
 
 class GDBRemoteCommunication : public Communication {
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to