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

https://reviews.llvm.org/D107840

Files:
  lldb/include/lldb/Utility/StringExtractorGDBRemote.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
  lldb/source/Utility/StringExtractorGDBRemote.cpp
  lldb/test/API/tools/lldb-server/TestGdbRemotePlatformFile.py

Index: lldb/test/API/tools/lldb-server/TestGdbRemotePlatformFile.py
===================================================================
--- lldb/test/API/tools/lldb-server/TestGdbRemotePlatformFile.py
+++ lldb/test/API/tools/lldb-server/TestGdbRemotePlatformFile.py
@@ -9,8 +9,39 @@
 from gdbremote_testcase import GdbRemoteTestCaseBase
 
 import binascii
+import os
 import stat
+import struct
 import tempfile
+import typing
+
+
+class GDBStat(typing.NamedTuple):
+    st_dev: int
+    st_ino: int
+    st_mode: int
+    st_nlink: int
+    st_uid: int
+    st_gid: int
+    st_rdev: int
+    st_size: int
+    st_blksize: int
+    st_blocks: int
+    st_atime: int
+    st_mtime: int
+    st_ctime: int
+
+
+def uint32_or_zero(x):
+    return x if x < 2**32 else 0
+
+
+def uint32_or_max(x):
+    return x if x < 2**32 else 2**32 - 1
+
+
+def uint32_trunc(x):
+    return x & (2**32 - 1)
 
 
 class TestGdbRemotePlatformFile(GdbRemoteTestCaseBase):
@@ -174,6 +205,71 @@
                 True)
             self.expect_gdbremote_sequence()
 
+    @skipIfWindows
+    @add_test_categories(["llgs"])
+    def test_platform_file_fstat(self):
+        server = self.connect_to_debug_monitor()
+        self.assertIsNotNone(server)
+
+        with tempfile.NamedTemporaryFile() as temp_file:
+            temp_file.write(b"some test data for stat")
+            temp_file.flush()
+
+            self.do_handshake()
+            self.test_sequence.add_log_lines(
+                ["read packet: $vFile:open:%s,0,0#00" % (
+                    binascii.b2a_hex(temp_file.name.encode()).decode(),),
+                 {"direction": "send",
+                 "regex": r"^\$F([0-9a-fA-F]+)#[0-9a-fA-F]{2}$",
+                 "capture": {1: "fd"}}],
+                True)
+
+            context = self.expect_gdbremote_sequence()
+            self.assertIsNotNone(context)
+            fd = int(context["fd"], 16)
+
+            self.reset_test_sequence()
+            self.test_sequence.add_log_lines(
+                ["read packet: $vFile:fstat:%x#00" % (fd,),
+                 {"direction": "send",
+                 "regex": r"^\$F([0-9a-fA-F]+);(.*)#[0-9a-fA-F]{2}$",
+                 "capture": {1: "size", 2: "data"}}],
+                True)
+            context = self.expect_gdbremote_sequence()
+            self.assertEqual(int(context["size"], 16), 64)
+            # NB: we're using .encode() as a hack because the test suite
+            # is wrongly using (unicode) str instead of bytes
+            gdb_stat = GDBStat(
+                *struct.unpack(">IIIIIIIQQQIII",
+                               self.decode_gdbremote_binary(context["data"])
+                                .encode("iso-8859-1")))
+            sys_stat = os.fstat(temp_file.fileno())
+
+            self.assertEqual(gdb_stat.st_dev, uint32_or_zero(sys_stat.st_dev))
+            self.assertEqual(gdb_stat.st_ino, uint32_or_zero(sys_stat.st_ino))
+            self.assertEqual(gdb_stat.st_mode, uint32_trunc(sys_stat.st_mode))
+            self.assertEqual(gdb_stat.st_nlink, uint32_or_max(sys_stat.st_nlink))
+            self.assertEqual(gdb_stat.st_uid, uint32_or_zero(sys_stat.st_uid))
+            self.assertEqual(gdb_stat.st_gid, uint32_or_zero(sys_stat.st_gid))
+            self.assertEqual(gdb_stat.st_rdev, uint32_or_zero(sys_stat.st_rdev))
+            self.assertEqual(gdb_stat.st_size, sys_stat.st_size)
+            self.assertEqual(gdb_stat.st_blksize, sys_stat.st_blksize)
+            self.assertEqual(gdb_stat.st_blocks, sys_stat.st_blocks)
+            self.assertEqual(gdb_stat.st_atime,
+                             uint32_or_zero(int(sys_stat.st_atime)))
+            self.assertEqual(gdb_stat.st_mtime,
+                             uint32_or_zero(int(sys_stat.st_mtime)))
+            self.assertEqual(gdb_stat.st_ctime,
+                             uint32_or_zero(int(sys_stat.st_ctime)))
+
+            self.reset_test_sequence()
+            self.test_sequence.add_log_lines(
+                ["read packet: $vFile:close:%x#00" % (fd,),
+                 "send packet: $F0#00"],
+                True)
+            self.expect_gdbremote_sequence()
+
+
     def expect_error(self):
         self.test_sequence.add_log_lines(
             [{"direction": "send",
Index: lldb/source/Utility/StringExtractorGDBRemote.cpp
===================================================================
--- lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -337,6 +337,8 @@
         return eServerPacketType_vFile_size;
       else if (PACKET_STARTS_WITH("vFile:exists"))
         return eServerPacketType_vFile_exists;
+      else if (PACKET_STARTS_WITH("vFile:fstat"))
+        return eServerPacketType_vFile_fstat;
       else if (PACKET_STARTS_WITH("vFile:stat"))
         return eServerPacketType_vFile_stat;
       else if (PACKET_STARTS_WITH("vFile:mode"))
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
@@ -71,6 +71,8 @@
 
   PacketResult Handle_vFile_unlink(StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_vFile_FStat(StringExtractorGDBRemote &packet);
+
   PacketResult Handle_vFile_Stat(StringExtractorGDBRemote &packet);
 
   PacketResult Handle_vFile_MD5(StringExtractorGDBRemote &packet);
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
@@ -29,6 +29,7 @@
 #include "lldb/Symbol/ObjectFile.h"
 #include "lldb/Target/Platform.h"
 #include "lldb/Utility/Endian.h"
+#include "lldb/Utility/DataEncoder.h"
 #include "lldb/Utility/GDBRemote.h"
 #include "lldb/Utility/Log.h"
 #include "lldb/Utility/StreamString.h"
@@ -156,6 +157,9 @@
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_vFile_size,
       &GDBRemoteCommunicationServerCommon::Handle_vFile_Size);
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_vFile_fstat,
+      &GDBRemoteCommunicationServerCommon::Handle_vFile_FStat);
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_vFile_stat,
       &GDBRemoteCommunicationServerCommon::Handle_vFile_Stat);
@@ -755,6 +759,56 @@
   return SendErrorResponse(24);
 }
 
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_FStat(
+    StringExtractorGDBRemote &packet) {
+  StreamGDBRemote response;
+  packet.SetFilePos(::strlen("vFile:fstat:"));
+  int fd = packet.GetS32(-1, 16);
+
+  struct stat file_stats;
+  if (::fstat(fd, &file_stats) == -1) {
+    const int save_errno = errno;
+    response.Printf("F-1,%x", save_errno);
+    return SendPacketNoLock(response.GetString());
+  }
+
+  std::array<char, 64> data;
+  DataEncoder encoder{data.data(), data.size(), lldb::eByteOrderBig,
+                      sizeof(void *)};
+  uint32_t offset = 0;
+
+  offset = encoder.PutUnsigned(
+      offset, 4, file_stats.st_dev <= UINT32_MAX ? file_stats.st_dev : 0);
+  offset = encoder.PutUnsigned(
+      offset, 4, file_stats.st_ino <= UINT32_MAX ? file_stats.st_ino : 0);
+  offset =
+      encoder.PutUnsigned(offset, 4, static_cast<uint32_t>(file_stats.st_mode));
+  offset = encoder.PutUnsigned(
+      offset, 4,
+      file_stats.st_nlink <= UINT32_MAX ? file_stats.st_nlink : UINT32_MAX);
+  offset = encoder.PutUnsigned(
+      offset, 4, file_stats.st_uid <= UINT32_MAX ? file_stats.st_uid : 0);
+  offset = encoder.PutUnsigned(
+      offset, 4, file_stats.st_gid <= UINT32_MAX ? file_stats.st_gid : 0);
+  offset = encoder.PutUnsigned(
+      offset, 4, file_stats.st_rdev <= UINT32_MAX ? file_stats.st_rdev : 0);
+  offset = encoder.PutUnsigned(offset, 8, file_stats.st_size);
+  offset = encoder.PutUnsigned(offset, 8, file_stats.st_blksize);
+  offset = encoder.PutUnsigned(offset, 8, file_stats.st_blocks);
+  offset = encoder.PutUnsigned(
+      offset, 4, file_stats.st_atime <= UINT32_MAX ? file_stats.st_atime : 0);
+  offset = encoder.PutUnsigned(
+      offset, 4, file_stats.st_mtime <= UINT32_MAX ? file_stats.st_mtime : 0);
+  offset = encoder.PutUnsigned(
+      offset, 4, file_stats.st_ctime <= UINT32_MAX ? file_stats.st_ctime : 0);
+  assert(offset == 64);
+
+  response.Printf("F%zx;", data.size());
+  response.PutEscapedBytes(data.data(), data.size());
+  return SendPacketNoLock(response.GetString());
+}
+
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerCommon::Handle_vFile_Stat(
     StringExtractorGDBRemote &packet) {
Index: lldb/include/lldb/Utility/StringExtractorGDBRemote.h
===================================================================
--- lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -88,6 +88,7 @@
     eServerPacketType_vFile_mode,
     eServerPacketType_vFile_exists,
     eServerPacketType_vFile_md5,
+    eServerPacketType_vFile_fstat,
     eServerPacketType_vFile_stat,
     eServerPacketType_vFile_symlink,
     eServerPacketType_vFile_unlink,
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to