diff --git a/lldb/include/lldb/Interpreter/CommandCompletions.h b/lldb/include/lldb/Interpreter/CommandCompletions.h --- a/lldb/include/lldb/Interpreter/CommandCompletions.h +++ b/lldb/include/lldb/Interpreter/CommandCompletions.h @@ -45,10 +45,12 @@ eThreadIndexCompletion = (1u << 17), eWatchPointIDCompletion = (1u << 18), eBreakpointNameCompletion = (1u << 19), + eProcessIDCompletion = (1u << 20), + eProcessNameCompletion = (1u << 21), // 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 << 20) + eCustomCompletion = (1u << 22) }; static bool InvokeCommonCompletionCallbacks( @@ -110,6 +112,12 @@ CompletionRequest &request, SearchFilter *searcher); + static void ProcessIDs(CommandInterpreter &interpreter, + CompletionRequest &request, SearchFilter *searcher); + + static void ProcessNames(CommandInterpreter &interpreter, + CompletionRequest &request, SearchFilter *searcher); + static void DisassemblyFlavors(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher); diff --git a/lldb/source/Commands/CommandCompletions.cpp b/lldb/source/Commands/CommandCompletions.cpp --- a/lldb/source/Commands/CommandCompletions.cpp +++ b/lldb/source/Commands/CommandCompletions.cpp @@ -71,6 +71,8 @@ {eThreadIndexCompletion, CommandCompletions::ThreadIndexes}, {eWatchPointIDCompletion, CommandCompletions::WatchPointIDs}, {eBreakpointNameCompletion, CommandCompletions::BreakpointNames}, + {eProcessIDCompletion, CommandCompletions::ProcessIDs}, + {eProcessNameCompletion, CommandCompletions::ProcessNames}, {eNoCompletion, nullptr} // This one has to be last in the list. }; @@ -649,6 +651,33 @@ } } +void CommandCompletions::ProcessIDs(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher) { + lldb::PlatformSP platform_sp(interpreter.GetPlatform(true)); + if (!platform_sp) + return; + ProcessInstanceInfoList process_infos; + ProcessInstanceInfoMatch match_info; + platform_sp->FindProcesses(match_info, process_infos); + for (const ProcessInstanceInfo &info : process_infos) + request.TryCompleteCurrentArg(std::to_string(info.GetProcessID()), + info.GetNameAsStringRef()); +} + +void CommandCompletions::ProcessNames(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher) { + lldb::PlatformSP platform_sp(interpreter.GetPlatform(true)); + if (!platform_sp) + return; + ProcessInstanceInfoList process_infos; + ProcessInstanceInfoMatch match_info; + platform_sp->FindProcesses(match_info, process_infos); + for (const ProcessInstanceInfo &info : process_infos) + request.TryCompleteCurrentArg(info.GetNameAsStringRef()); +} + void CommandCompletions::TypeLanguages(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher) { diff --git a/lldb/source/Commands/CommandObjectPlatform.cpp b/lldb/source/Commands/CommandObjectPlatform.cpp --- a/lldb/source/Commands/CommandObjectPlatform.cpp +++ b/lldb/source/Commands/CommandObjectPlatform.cpp @@ -1331,6 +1331,14 @@ ~CommandObjectPlatformProcessInfo() override = default; + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), CommandCompletions::eProcessIDCompletion, + request, nullptr); + } + protected: bool DoExecute(Args &args, CommandReturnObject &result) override { Target *target = GetDebugger().GetSelectedTarget().get(); @@ -1447,46 +1455,6 @@ return llvm::makeArrayRef(g_platform_process_attach_options); } - void HandleOptionArgumentCompletion( - CompletionRequest &request, OptionElementVector &opt_element_vector, - int opt_element_index, CommandInterpreter &interpreter) override { - int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos; - int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index; - - // We are only completing the name option for now... - - // Are we in the name? - if (GetDefinitions()[opt_defs_index].short_option != 'n') - return; - - // Look to see if there is a -P argument provided, and if so use that - // plugin, otherwise use the default plugin. - - const char *partial_name = nullptr; - partial_name = request.GetParsedLine().GetArgumentAtIndex(opt_arg_pos); - - PlatformSP platform_sp(interpreter.GetPlatform(true)); - if (!platform_sp) - return; - - ProcessInstanceInfoList process_infos; - ProcessInstanceInfoMatch match_info; - if (partial_name) { - match_info.GetProcessInfo().GetExecutableFile().SetFile( - partial_name, FileSpec::Style::native); - match_info.SetNameMatchType(NameMatch::StartsWith); - } - platform_sp->FindProcesses(match_info, process_infos); - const uint32_t num_matches = process_infos.size(); - if (num_matches == 0) - return; - - for (uint32_t i = 0; i < num_matches; ++i) { - request.AddCompletion(process_infos[i].GetNameAsStringRef()); - } - return; - } - // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -320,49 +320,6 @@ return llvm::makeArrayRef(g_process_attach_options); } - void HandleOptionArgumentCompletion( - CompletionRequest &request, OptionElementVector &opt_element_vector, - int opt_element_index, CommandInterpreter &interpreter) override { - int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos; - int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index; - - switch (GetDefinitions()[opt_defs_index].short_option) { - case 'n': { - // Look to see if there is a -P argument provided, and if so use that - // plugin, otherwise use the default plugin. - - const char *partial_name = nullptr; - partial_name = request.GetParsedLine().GetArgumentAtIndex(opt_arg_pos); - - PlatformSP platform_sp(interpreter.GetPlatform(true)); - if (!platform_sp) - return; - ProcessInstanceInfoList process_infos; - ProcessInstanceInfoMatch match_info; - if (partial_name) { - match_info.GetProcessInfo().GetExecutableFile().SetFile( - partial_name, FileSpec::Style::native); - match_info.SetNameMatchType(NameMatch::StartsWith); - } - platform_sp->FindProcesses(match_info, process_infos); - const size_t num_matches = process_infos.size(); - if (num_matches == 0) - return; - for (size_t i = 0; i < num_matches; ++i) { - request.AddCompletion(process_infos[i].GetNameAsStringRef()); - } - } break; - - case 'P': - CommandCompletions::InvokeCommonCompletionCallbacks( - interpreter, CommandCompletions::eProcessPluginCompletion, request, - nullptr); - break; - } - } - - // Instance variables to hold the values for command options. - ProcessAttachInfo attach_info; }; diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -1078,9 +1078,9 @@ { eArgTypePath, "path", CommandCompletions::eDiskFileCompletion, { nullptr, false }, "Path." }, { eArgTypePermissionsNumber, "perms-numeric", CommandCompletions::eNoCompletion, { nullptr, false }, "Permissions given as an octal number (e.g. 755)." }, { eArgTypePermissionsString, "perms=string", CommandCompletions::eNoCompletion, { nullptr, false }, "Permissions given as a string value (e.g. rw-r-xr--)." }, - { eArgTypePid, "pid", CommandCompletions::eNoCompletion, { nullptr, false }, "The process ID number." }, + { eArgTypePid, "pid", CommandCompletions::eProcessIDCompletion, { nullptr, false }, "The process ID number." }, { eArgTypePlugin, "plugin", CommandCompletions::eProcessPluginCompletion, { nullptr, false }, "Help text goes here." }, - { eArgTypeProcessName, "process-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of the process." }, + { eArgTypeProcessName, "process-name", CommandCompletions::eProcessNameCompletion, { nullptr, false }, "The name of the process." }, { eArgTypePythonClass, "python-class", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a Python class." }, { eArgTypePythonFunction, "python-function", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a Python function." }, { eArgTypePythonScript, "python-script", CommandCompletions::eNoCompletion, { nullptr, false }, "Source code written in Python." }, diff --git a/lldb/test/API/functionalities/completion/TestCompletion.py b/lldb/test/API/functionalities/completion/TestCompletion.py --- a/lldb/test/API/functionalities/completion/TestCompletion.py +++ b/lldb/test/API/functionalities/completion/TestCompletion.py @@ -5,6 +5,8 @@ import os +from multiprocessing import Process +import psutil import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * @@ -117,6 +119,27 @@ self.complete_from_to('process ' + subcommand + ' mac', 'process ' + subcommand + ' mach-o-core') + @skipIfRemote + def test_common_completion_process_pid_and_name(self): + # The LLDB process itself and the process already attached to are both + # ignored by the process discovery mechanism, thus we need a process known + # to us here. + self.build() + server = self.spawnSubprocess( + self.getBuildArtifact("a.out"), + ["-x"], # Arg "-x" makes the subprocess wait for input thus it won't be terminated too early + install_remote=False) + self.assertIsNotNone(server) + pid = server.pid + + self.complete_from_to('process attach -p ', [str(pid)]) + self.complete_from_to('platform process attach -p ', [str(pid)]) + self.complete_from_to('platform process info ', [str(pid)]) + + pname = psutil.Process(pid).name() # FIXME: psutil doesn't work for remote + self.complete_from_to('process attach -n ', [str(pname)]) + self.complete_from_to('platform process attach -n ', [str(pname)]) + def test_process_signal(self): # The tab completion for "process signal" won't work without a running process. self.complete_from_to('process signal ', diff --git a/lldb/test/API/functionalities/completion/main.cpp b/lldb/test/API/functionalities/completion/main.cpp --- a/lldb/test/API/functionalities/completion/main.cpp +++ b/lldb/test/API/functionalities/completion/main.cpp @@ -1,3 +1,5 @@ +#include + class Foo { public: @@ -11,14 +13,16 @@ struct Container { int MemberVar; }; -int main() -{ - Foo fooo; - Foo *ptr_fooo = &fooo; - fooo.Bar(1, 2); +int main(int argc, char *argv[]) { + if (argc > 1 && std::string(argv[1]) == "-x") + std::cin.get(); + + Foo fooo; + Foo *ptr_fooo = &fooo; + fooo.Bar(1, 2); - Container container; - Container *ptr_container = &container; - int q = Quux(); - return container.MemberVar = 3; // Break here + Container container; + Container *ptr_container = &container; + int q = Quux(); + return container.MemberVar = 3; // Break here }