Skip to content

Commit 7e4e29e

Browse files
MrHateTeemperor
authored andcommitted
[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 (cherry picked from commit 3cd8d7b)
1 parent 6fbb8d8 commit 7e4e29e

16 files changed

+278
-2
lines changed

lldb/docs/lldb-platform-packets.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,27 @@ incompatible with the flags that gdb specifies.
237237
// Continues to return the results of the qfProcessInfo. Once all matches
238238
// have been sent, Exx is returned to indicate end of matches.
239239

240+
//----------------------------------------------------------------------
241+
// qPathComplete
242+
//
243+
// BRIEF
244+
// Get a list of matched disk files/directories by passing a boolean flag
245+
// and a partial path.
246+
//
247+
// EXAMPLE
248+
//
249+
// receive: qPathComplete:0,6d61696e
250+
// send: M6d61696e2e637070
251+
// receive: qPathComplete:1,746573
252+
// send: M746573742f,74657374732f
253+
//
254+
// If the first argument is zero, the result should contain all
255+
// files (including directories) starting with the given path. If the
256+
// argument is one, the result should contain only directories.
257+
//
258+
// The result should be a comma-separated list of hex-encoded paths.
259+
// Paths denoting a directory should end with a directory separator ('/' or '\').
260+
240261
//----------------------------------------------------------------------
241262
// vFile:size:
242263
//

lldb/include/lldb/Interpreter/CommandCompletions.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@ class CommandCompletions {
4747
eBreakpointNameCompletion = (1u << 19),
4848
eProcessIDCompletion = (1u << 20),
4949
eProcessNameCompletion = (1u << 21),
50+
eRemoteDiskFileCompletion = (1u << 22),
51+
eRemoteDiskDirectoryCompletion = (1u << 23),
5052
// This item serves two purposes. It is the last element in the enum, so
5153
// you can add custom enums starting from here in your Option class. Also
5254
// if you & in this bit the base code will not process the option.
53-
eCustomCompletion = (1u << 22)
55+
eCustomCompletion = (1u << 24)
5456
};
5557

5658
static bool InvokeCommonCompletionCallbacks(
@@ -72,6 +74,14 @@ class CommandCompletions {
7274
StringList &matches,
7375
TildeExpressionResolver &Resolver);
7476

77+
static void RemoteDiskFiles(CommandInterpreter &interpreter,
78+
CompletionRequest &request,
79+
SearchFilter *searcher);
80+
81+
static void RemoteDiskDirectories(CommandInterpreter &interpreter,
82+
CompletionRequest &request,
83+
SearchFilter *searcher);
84+
7585
static void SourceFiles(CommandInterpreter &interpreter,
7686
CompletionRequest &request, SearchFilter *searcher);
7787

lldb/include/lldb/Target/Platform.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,9 @@ class Platform : public PluginInterface {
523523
return UINT64_MAX;
524524
}
525525

526+
virtual void AutoCompleteDiskFileOrDirectory(CompletionRequest &request,
527+
bool only_dir) {}
528+
526529
virtual uint64_t ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst,
527530
uint64_t dst_len, Status &error) {
528531
error.SetErrorStringWithFormat(

lldb/include/lldb/Utility/StringExtractorGDBRemote.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class StringExtractorGDBRemote : public StringExtractor {
7676
eServerPacketType_QSetSTDERR,
7777
eServerPacketType_QSetWorkingDir,
7878
eServerPacketType_QStartNoAckMode,
79+
eServerPacketType_qPathComplete,
7980
eServerPacketType_qPlatform_shell,
8081
eServerPacketType_qPlatform_mkdir,
8182
eServerPacketType_qPlatform_chmod,

lldb/source/Commands/CommandCompletions.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ bool CommandCompletions::InvokeCommonCompletionCallbacks(
7373
{eBreakpointNameCompletion, CommandCompletions::BreakpointNames},
7474
{eProcessIDCompletion, CommandCompletions::ProcessIDs},
7575
{eProcessNameCompletion, CommandCompletions::ProcessNames},
76+
{eRemoteDiskFileCompletion, CommandCompletions::RemoteDiskFiles},
77+
{eRemoteDiskDirectoryCompletion, CommandCompletions::RemoteDiskDirectories},
7678
{eNoCompletion, nullptr} // This one has to be last in the list.
7779
};
7880

@@ -486,6 +488,24 @@ void CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name,
486488
DiskFilesOrDirectories(partial_file_name, true, matches, Resolver);
487489
}
488490

491+
void CommandCompletions::RemoteDiskFiles(CommandInterpreter &interpreter,
492+
CompletionRequest &request,
493+
SearchFilter *searcher) {
494+
lldb::PlatformSP platform_sp =
495+
interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
496+
if (platform_sp)
497+
platform_sp->AutoCompleteDiskFileOrDirectory(request, false);
498+
}
499+
500+
void CommandCompletions::RemoteDiskDirectories(CommandInterpreter &interpreter,
501+
CompletionRequest &request,
502+
SearchFilter *searcher) {
503+
lldb::PlatformSP platform_sp =
504+
interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
505+
if (platform_sp)
506+
platform_sp->AutoCompleteDiskFileOrDirectory(request, true);
507+
}
508+
489509
void CommandCompletions::Modules(CommandInterpreter &interpreter,
490510
CompletionRequest &request,
491511
SearchFilter *searcher) {

lldb/source/Commands/CommandObjectPlatform.cpp

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,8 @@ class CommandObjectPlatformSettings : public CommandObjectParsed {
392392
"or for a platform by name.",
393393
"platform settings", 0),
394394
m_options(),
395-
m_option_working_dir(LLDB_OPT_SET_1, false, "working-dir", 'w', 0,
395+
m_option_working_dir(LLDB_OPT_SET_1, false, "working-dir", 'w',
396+
CommandCompletions::eRemoteDiskDirectoryCompletion,
396397
eArgTypePath,
397398
"The working directory for the platform.") {
398399
m_options.Append(&m_option_working_dir, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
@@ -485,6 +486,15 @@ class CommandObjectPlatformFOpen : public CommandObjectParsed {
485486

486487
~CommandObjectPlatformFOpen() override = default;
487488

489+
void
490+
HandleArgumentCompletion(CompletionRequest &request,
491+
OptionElementVector &opt_element_vector) override {
492+
if (request.GetCursorIndex() == 0)
493+
CommandCompletions::InvokeCommonCompletionCallbacks(
494+
GetCommandInterpreter(),
495+
CommandCompletions::eRemoteDiskFileCompletion, request, nullptr);
496+
}
497+
488498
bool DoExecute(Args &args, CommandReturnObject &result) override {
489499
PlatformSP platform_sp(
490500
GetDebugger().GetPlatformList().GetSelectedPlatform());
@@ -817,6 +827,19 @@ class CommandObjectPlatformGetFile : public CommandObjectParsed {
817827

818828
~CommandObjectPlatformGetFile() override = default;
819829

830+
void
831+
HandleArgumentCompletion(CompletionRequest &request,
832+
OptionElementVector &opt_element_vector) override {
833+
if (request.GetCursorIndex() == 0)
834+
CommandCompletions::InvokeCommonCompletionCallbacks(
835+
GetCommandInterpreter(),
836+
CommandCompletions::eRemoteDiskFileCompletion, request, nullptr);
837+
else if (request.GetCursorIndex() == 1)
838+
CommandCompletions::InvokeCommonCompletionCallbacks(
839+
GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
840+
request, nullptr);
841+
}
842+
820843
bool DoExecute(Args &args, CommandReturnObject &result) override {
821844
// If the number of arguments is incorrect, issue an error message.
822845
if (args.GetArgumentCount() != 2) {
@@ -882,6 +905,17 @@ class CommandObjectPlatformGetSize : public CommandObjectParsed {
882905

883906
~CommandObjectPlatformGetSize() override = default;
884907

908+
void
909+
HandleArgumentCompletion(CompletionRequest &request,
910+
OptionElementVector &opt_element_vector) override {
911+
if (request.GetCursorIndex() != 0)
912+
return;
913+
914+
CommandCompletions::InvokeCommonCompletionCallbacks(
915+
GetCommandInterpreter(), CommandCompletions::eRemoteDiskFileCompletion,
916+
request, nullptr);
917+
}
918+
885919
bool DoExecute(Args &args, CommandReturnObject &result) override {
886920
// If the number of arguments is incorrect, issue an error message.
887921
if (args.GetArgumentCount() != 1) {
@@ -927,6 +961,19 @@ class CommandObjectPlatformPutFile : public CommandObjectParsed {
927961

928962
~CommandObjectPlatformPutFile() override = default;
929963

964+
void
965+
HandleArgumentCompletion(CompletionRequest &request,
966+
OptionElementVector &opt_element_vector) override {
967+
if (request.GetCursorIndex() == 0)
968+
CommandCompletions::InvokeCommonCompletionCallbacks(
969+
GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
970+
request, nullptr);
971+
else if (request.GetCursorIndex() == 1)
972+
CommandCompletions::InvokeCommonCompletionCallbacks(
973+
GetCommandInterpreter(),
974+
CommandCompletions::eRemoteDiskFileCompletion, request, nullptr);
975+
}
976+
930977
bool DoExecute(Args &args, CommandReturnObject &result) override {
931978
const char *src = args.GetArgumentAtIndex(0);
932979
const char *dst = args.GetArgumentAtIndex(1);

lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,11 @@ PlatformRemoteGDBServer::GetFileSize(const FileSpec &file_spec) {
661661
return m_gdb_client.GetFileSize(file_spec);
662662
}
663663

664+
void PlatformRemoteGDBServer::AutoCompleteDiskFileOrDirectory(
665+
CompletionRequest &request, bool only_dir) {
666+
m_gdb_client.AutoCompleteDiskFileOrDirectory(request, only_dir);
667+
}
668+
664669
uint64_t PlatformRemoteGDBServer::ReadFile(lldb::user_id_t fd, uint64_t offset,
665670
void *dst, uint64_t dst_len,
666671
Status &error) {

lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ class PlatformRemoteGDBServer : public Platform, private UserIDResolver {
127127

128128
lldb::user_id_t GetFileSize(const FileSpec &file_spec) override;
129129

130+
void AutoCompleteDiskFileOrDirectory(CompletionRequest &request,
131+
bool only_dir) override;
132+
130133
Status PutFile(const FileSpec &source, const FileSpec &destination,
131134
uint32_t uid = UINT32_MAX, uint32_t gid = UINT32_MAX) override;
132135

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2981,6 +2981,31 @@ lldb::user_id_t GDBRemoteCommunicationClient::GetFileSize(
29812981
return UINT64_MAX;
29822982
}
29832983

2984+
void GDBRemoteCommunicationClient::AutoCompleteDiskFileOrDirectory(
2985+
CompletionRequest &request, bool only_dir) {
2986+
lldb_private::StreamString stream;
2987+
stream.PutCString("qPathComplete:");
2988+
stream.PutHex32(only_dir ? 1 : 0);
2989+
stream.PutChar(',');
2990+
stream.PutStringAsRawHex8(request.GetCursorArgumentPrefix());
2991+
StringExtractorGDBRemote response;
2992+
if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
2993+
PacketResult::Success) {
2994+
StreamString strm;
2995+
char ch = response.GetChar();
2996+
if (ch != 'M')
2997+
return;
2998+
while (response.Peek()) {
2999+
strm.Clear();
3000+
while ((ch = response.GetHexU8(0, false)) != '\0')
3001+
strm.PutChar(ch);
3002+
request.AddCompletion(strm.GetString());
3003+
if (response.GetChar() != ',')
3004+
break;
3005+
}
3006+
}
3007+
}
3008+
29843009
Status
29853010
GDBRemoteCommunicationClient::GetFilePermissions(const FileSpec &file_spec,
29863011
uint32_t &file_permissions) {

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,9 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
375375

376376
lldb::user_id_t GetFileSize(const FileSpec &file_spec);
377377

378+
void AutoCompleteDiskFileOrDirectory(CompletionRequest &request,
379+
bool only_dir);
380+
378381
Status GetFilePermissions(const FileSpec &file_spec,
379382
uint32_t &file_permissions);
380383

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@
2626
#include "lldb/Host/FileAction.h"
2727
#include "lldb/Host/Host.h"
2828
#include "lldb/Host/HostInfo.h"
29+
#include "lldb/Interpreter/CommandCompletions.h"
2930
#include "lldb/Target/Platform.h"
3031
#include "lldb/Target/UnixSignals.h"
3132
#include "lldb/Utility/GDBRemote.h"
3233
#include "lldb/Utility/Log.h"
3334
#include "lldb/Utility/StreamString.h"
3435
#include "lldb/Utility/StructuredData.h"
36+
#include "lldb/Utility/TildeExpressionResolver.h"
3537
#include "lldb/Utility/UriParser.h"
3638

3739
#include "lldb/Utility/StringExtractorGDBRemote.h"
@@ -68,6 +70,9 @@ GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform(
6870
RegisterMemberFunctionHandler(
6971
StringExtractorGDBRemote::eServerPacketType_qProcessInfo,
7072
&GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo);
73+
RegisterMemberFunctionHandler(
74+
StringExtractorGDBRemote::eServerPacketType_qPathComplete,
75+
&GDBRemoteCommunicationServerPlatform::Handle_qPathComplete);
7176
RegisterMemberFunctionHandler(
7277
StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir,
7378
&GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir);
@@ -333,6 +338,38 @@ GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo(
333338
return SendPacketNoLock(response.GetString());
334339
}
335340

341+
GDBRemoteCommunication::PacketResult
342+
GDBRemoteCommunicationServerPlatform::Handle_qPathComplete(
343+
StringExtractorGDBRemote &packet) {
344+
packet.SetFilePos(::strlen("qPathComplete:"));
345+
const bool only_dir = (packet.GetHexMaxU32(false, 0) == 1);
346+
if (packet.GetChar() != ',')
347+
return SendErrorResponse(85);
348+
std::string path;
349+
packet.GetHexByteString(path);
350+
351+
StringList matches;
352+
StandardTildeExpressionResolver resolver;
353+
if (only_dir)
354+
CommandCompletions::DiskDirectories(path, matches, resolver);
355+
else
356+
CommandCompletions::DiskFiles(path, matches, resolver);
357+
358+
StreamString response;
359+
response.PutChar('M');
360+
llvm::StringRef separator;
361+
std::sort(matches.begin(), matches.end());
362+
for (const auto &match : matches) {
363+
response << separator;
364+
separator = ",";
365+
// encode result strings into hex bytes to avoid unexpected error caused by
366+
// special characters like '$'.
367+
response.PutStringAsRawHex8(match.c_str());
368+
}
369+
370+
return SendPacketNoLock(response.GetString());
371+
}
372+
336373
GDBRemoteCommunication::PacketResult
337374
GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir(
338375
StringExtractorGDBRemote &packet) {

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ class GDBRemoteCommunicationServerPlatform
8181

8282
PacketResult Handle_qKillSpawnedProcess(StringExtractorGDBRemote &packet);
8383

84+
PacketResult Handle_qPathComplete(StringExtractorGDBRemote &packet);
85+
8486
PacketResult Handle_qProcessInfo(StringExtractorGDBRemote &packet);
8587

8688
PacketResult Handle_qGetWorkingDir(StringExtractorGDBRemote &packet);

lldb/source/Utility/StringExtractorGDBRemote.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ StringExtractorGDBRemote::GetServerPacketType() const {
233233
return eServerPacketType_qPlatform_chmod;
234234
if (PACKET_MATCHES("qProcessInfo"))
235235
return eServerPacketType_qProcessInfo;
236+
if (PACKET_STARTS_WITH("qPathComplete:"))
237+
return eServerPacketType_qPathComplete;
236238
break;
237239

238240
case 'Q':
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from gdbclientutils import *
2+
3+
class TestGDBRemoteDiskFileCompletion(GDBRemoteTestBase):
4+
5+
def test_autocomplete_request(self):
6+
"""Test remote disk completion on remote-gdb-server plugin"""
7+
8+
class Responder(MockGDBServerResponder):
9+
def qPathComplete(self):
10+
return "M{},{}".format(
11+
"test".encode().hex(),
12+
"123".encode().hex()
13+
)
14+
15+
self.server.responder = Responder()
16+
17+
try:
18+
self.runCmd("platform select remote-gdb-server")
19+
self.runCmd("platform connect connect://localhost:%d" %
20+
self.server.port)
21+
self.assertTrue(self.dbg.GetSelectedPlatform().IsConnected())
22+
23+
self.complete_from_to('platform get-size ', ['test', '123'])
24+
self.complete_from_to('platform get-file ', ['test', '123'])
25+
self.complete_from_to('platform put-file foo ', ['test', '123'])
26+
self.complete_from_to('platform file open ', ['test', '123'])
27+
self.complete_from_to('platform settings -w ', ['test', '123'])
28+
finally:
29+
self.dbg.GetSelectedPlatform().DisconnectRemote()

lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ def respond(self, packet):
178178
return self.qsProcessInfo()
179179
if packet.startswith("qfProcessInfo"):
180180
return self.qfProcessInfo(packet)
181+
if packet.startswith("qPathComplete:"):
182+
return self.qPathComplete()
181183

182184
return self.other(packet)
183185

@@ -282,6 +284,9 @@ def QListThreadsInStopReply(self):
282284
def qMemoryRegionInfo(self):
283285
return ""
284286

287+
def qPathComplete(self):
288+
return ""
289+
285290
"""
286291
Raised when we receive a packet for which there is no default action.
287292
Override the responder class to implement behavior suitable for the test at

0 commit comments

Comments
 (0)