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 { 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 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 +GDBRemoteCommunicationClient::FStat(lldb::user_id_t fd) { + lldb_private::StreamString stream; + stream.Printf("vFile:fstat:%" PRIx64, 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 == UINT64_MAX) + return UINT64_MAX; + llvm::Optional st = FStat(fd); + CloseFile(fd, error); + if (st) + return st->gdb_st_size; + } } return UINT64_MAX; } 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()