1 //===-- CommandCompletions.cpp --------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "llvm/ADT/SmallString.h"
10 #include "llvm/ADT/StringSet.h"
11
12 #include "lldb/Breakpoint/Watchpoint.h"
13 #include "lldb/Core/FileSpecList.h"
14 #include "lldb/Core/Module.h"
15 #include "lldb/Core/PluginManager.h"
16 #include "lldb/DataFormatters/DataVisualization.h"
17 #include "lldb/Host/FileSystem.h"
18 #include "lldb/Interpreter/CommandCompletions.h"
19 #include "lldb/Interpreter/CommandInterpreter.h"
20 #include "lldb/Interpreter/OptionValueProperties.h"
21 #include "lldb/Symbol/CompileUnit.h"
22 #include "lldb/Symbol/Variable.h"
23 #include "lldb/Target/Language.h"
24 #include "lldb/Target/Process.h"
25 #include "lldb/Target/RegisterContext.h"
26 #include "lldb/Target/Thread.h"
27 #include "lldb/Utility/FileSpec.h"
28 #include "lldb/Utility/StreamString.h"
29 #include "lldb/Utility/TildeExpressionResolver.h"
30
31 #include "llvm/Support/FileSystem.h"
32 #include "llvm/Support/Path.h"
33
34 using namespace lldb_private;
35
36 // This is the command completion callback that is used to complete the
37 // argument of the option it is bound to (in the OptionDefinition table
38 // below).
39 typedef void (*CompletionCallback)(CommandInterpreter &interpreter,
40 CompletionRequest &request,
41 // A search filter to limit the search...
42 lldb_private::SearchFilter *searcher);
43
44 struct CommonCompletionElement {
45 uint32_t type;
46 CompletionCallback callback;
47 };
48
InvokeCommonCompletionCallbacks(CommandInterpreter & interpreter,uint32_t completion_mask,CompletionRequest & request,SearchFilter * searcher)49 bool CommandCompletions::InvokeCommonCompletionCallbacks(
50 CommandInterpreter &interpreter, uint32_t completion_mask,
51 CompletionRequest &request, SearchFilter *searcher) {
52 bool handled = false;
53
54 const CommonCompletionElement common_completions[] = {
55 {eSourceFileCompletion, CommandCompletions::SourceFiles},
56 {eDiskFileCompletion, CommandCompletions::DiskFiles},
57 {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories},
58 {eSymbolCompletion, CommandCompletions::Symbols},
59 {eModuleCompletion, CommandCompletions::Modules},
60 {eModuleUUIDCompletion, CommandCompletions::ModuleUUIDs},
61 {eSettingsNameCompletion, CommandCompletions::SettingsNames},
62 {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames},
63 {eArchitectureCompletion, CommandCompletions::ArchitectureNames},
64 {eVariablePathCompletion, CommandCompletions::VariablePath},
65 {eRegisterCompletion, CommandCompletions::Registers},
66 {eBreakpointCompletion, CommandCompletions::Breakpoints},
67 {eProcessPluginCompletion, CommandCompletions::ProcessPluginNames},
68 {eDisassemblyFlavorCompletion, CommandCompletions::DisassemblyFlavors},
69 {eTypeLanguageCompletion, CommandCompletions::TypeLanguages},
70 {eFrameIndexCompletion, CommandCompletions::FrameIndexes},
71 {eStopHookIDCompletion, CommandCompletions::StopHookIDs},
72 {eThreadIndexCompletion, CommandCompletions::ThreadIndexes},
73 {eWatchPointIDCompletion, CommandCompletions::WatchPointIDs},
74 {eBreakpointNameCompletion, CommandCompletions::BreakpointNames},
75 {eProcessIDCompletion, CommandCompletions::ProcessIDs},
76 {eProcessNameCompletion, CommandCompletions::ProcessNames},
77 {eRemoteDiskFileCompletion, CommandCompletions::RemoteDiskFiles},
78 {eRemoteDiskDirectoryCompletion,
79 CommandCompletions::RemoteDiskDirectories},
80 {eTypeCategoryNameCompletion, CommandCompletions::TypeCategoryNames},
81 {eNoCompletion, nullptr} // This one has to be last in the list.
82 };
83
84 for (int i = 0;; i++) {
85 if (common_completions[i].type == eNoCompletion)
86 break;
87 else if ((common_completions[i].type & completion_mask) ==
88 common_completions[i].type &&
89 common_completions[i].callback != nullptr) {
90 handled = true;
91 common_completions[i].callback(interpreter, request, searcher);
92 }
93 }
94 return handled;
95 }
96
97 namespace {
98 // The Completer class is a convenient base class for building searchers that
99 // go along with the SearchFilter passed to the standard Completer functions.
100 class Completer : public Searcher {
101 public:
Completer(CommandInterpreter & interpreter,CompletionRequest & request)102 Completer(CommandInterpreter &interpreter, CompletionRequest &request)
103 : m_interpreter(interpreter), m_request(request) {}
104
105 ~Completer() override = default;
106
107 CallbackReturn SearchCallback(SearchFilter &filter, SymbolContext &context,
108 Address *addr) override = 0;
109
110 lldb::SearchDepth GetDepth() override = 0;
111
112 virtual void DoCompletion(SearchFilter *filter) = 0;
113
114 protected:
115 CommandInterpreter &m_interpreter;
116 CompletionRequest &m_request;
117
118 private:
119 Completer(const Completer &) = delete;
120 const Completer &operator=(const Completer &) = delete;
121 };
122 } // namespace
123
124 // SourceFileCompleter implements the source file completer
125 namespace {
126 class SourceFileCompleter : public Completer {
127 public:
SourceFileCompleter(CommandInterpreter & interpreter,CompletionRequest & request)128 SourceFileCompleter(CommandInterpreter &interpreter,
129 CompletionRequest &request)
130 : Completer(interpreter, request), m_matching_files() {
131 FileSpec partial_spec(m_request.GetCursorArgumentPrefix());
132 m_file_name = partial_spec.GetFilename().GetCString();
133 m_dir_name = partial_spec.GetDirectory().GetCString();
134 }
135
GetDepth()136 lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthCompUnit; }
137
SearchCallback(SearchFilter & filter,SymbolContext & context,Address * addr)138 Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
139 SymbolContext &context,
140 Address *addr) override {
141 if (context.comp_unit != nullptr) {
142 const char *cur_file_name =
143 context.comp_unit->GetPrimaryFile().GetFilename().GetCString();
144 const char *cur_dir_name =
145 context.comp_unit->GetPrimaryFile().GetDirectory().GetCString();
146
147 bool match = false;
148 if (m_file_name && cur_file_name &&
149 strstr(cur_file_name, m_file_name) == cur_file_name)
150 match = true;
151
152 if (match && m_dir_name && cur_dir_name &&
153 strstr(cur_dir_name, m_dir_name) != cur_dir_name)
154 match = false;
155
156 if (match) {
157 m_matching_files.AppendIfUnique(context.comp_unit->GetPrimaryFile());
158 }
159 }
160 return Searcher::eCallbackReturnContinue;
161 }
162
DoCompletion(SearchFilter * filter)163 void DoCompletion(SearchFilter *filter) override {
164 filter->Search(*this);
165 // Now convert the filelist to completions:
166 for (size_t i = 0; i < m_matching_files.GetSize(); i++) {
167 m_request.AddCompletion(
168 m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString());
169 }
170 }
171
172 private:
173 FileSpecList m_matching_files;
174 const char *m_file_name;
175 const char *m_dir_name;
176
177 SourceFileCompleter(const SourceFileCompleter &) = delete;
178 const SourceFileCompleter &operator=(const SourceFileCompleter &) = delete;
179 };
180 } // namespace
181
regex_chars(const char comp)182 static bool regex_chars(const char comp) {
183 return llvm::StringRef("[](){}+.*|^$\\?").contains(comp);
184 }
185
186 namespace {
187 class SymbolCompleter : public Completer {
188
189 public:
SymbolCompleter(CommandInterpreter & interpreter,CompletionRequest & request)190 SymbolCompleter(CommandInterpreter &interpreter, CompletionRequest &request)
191 : Completer(interpreter, request) {
192 std::string regex_str;
193 if (!m_request.GetCursorArgumentPrefix().empty()) {
194 regex_str.append("^");
195 regex_str.append(std::string(m_request.GetCursorArgumentPrefix()));
196 } else {
197 // Match anything since the completion string is empty
198 regex_str.append(".");
199 }
200 std::string::iterator pos =
201 find_if(regex_str.begin() + 1, regex_str.end(), regex_chars);
202 while (pos < regex_str.end()) {
203 pos = regex_str.insert(pos, '\\');
204 pos = find_if(pos + 2, regex_str.end(), regex_chars);
205 }
206 m_regex = RegularExpression(regex_str);
207 }
208
GetDepth()209 lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }
210
SearchCallback(SearchFilter & filter,SymbolContext & context,Address * addr)211 Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
212 SymbolContext &context,
213 Address *addr) override {
214 if (context.module_sp) {
215 SymbolContextList sc_list;
216 const bool include_symbols = true;
217 const bool include_inlines = true;
218 context.module_sp->FindFunctions(m_regex, include_symbols,
219 include_inlines, sc_list);
220
221 SymbolContext sc;
222 // Now add the functions & symbols to the list - only add if unique:
223 for (uint32_t i = 0; i < sc_list.GetSize(); i++) {
224 if (sc_list.GetContextAtIndex(i, sc)) {
225 ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled);
226 // Ensure that the function name matches the regex. This is more than
227 // a sanity check. It is possible that the demangled function name
228 // does not start with the prefix, for example when it's in an
229 // anonymous namespace.
230 if (!func_name.IsEmpty() && m_regex.Execute(func_name.GetStringRef()))
231 m_match_set.insert(func_name);
232 }
233 }
234 }
235 return Searcher::eCallbackReturnContinue;
236 }
237
DoCompletion(SearchFilter * filter)238 void DoCompletion(SearchFilter *filter) override {
239 filter->Search(*this);
240 collection::iterator pos = m_match_set.begin(), end = m_match_set.end();
241 for (pos = m_match_set.begin(); pos != end; pos++)
242 m_request.AddCompletion((*pos).GetCString());
243 }
244
245 private:
246 RegularExpression m_regex;
247 typedef std::set<ConstString> collection;
248 collection m_match_set;
249
250 SymbolCompleter(const SymbolCompleter &) = delete;
251 const SymbolCompleter &operator=(const SymbolCompleter &) = delete;
252 };
253 } // namespace
254
255 namespace {
256 class ModuleCompleter : public Completer {
257 public:
ModuleCompleter(CommandInterpreter & interpreter,CompletionRequest & request)258 ModuleCompleter(CommandInterpreter &interpreter, CompletionRequest &request)
259 : Completer(interpreter, request) {
260 FileSpec partial_spec(m_request.GetCursorArgumentPrefix());
261 m_file_name = partial_spec.GetFilename().GetCString();
262 m_dir_name = partial_spec.GetDirectory().GetCString();
263 }
264
GetDepth()265 lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }
266
SearchCallback(SearchFilter & filter,SymbolContext & context,Address * addr)267 Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
268 SymbolContext &context,
269 Address *addr) override {
270 if (context.module_sp) {
271 const char *cur_file_name =
272 context.module_sp->GetFileSpec().GetFilename().GetCString();
273 const char *cur_dir_name =
274 context.module_sp->GetFileSpec().GetDirectory().GetCString();
275
276 bool match = false;
277 if (m_file_name && cur_file_name &&
278 strstr(cur_file_name, m_file_name) == cur_file_name)
279 match = true;
280
281 if (match && m_dir_name && cur_dir_name &&
282 strstr(cur_dir_name, m_dir_name) != cur_dir_name)
283 match = false;
284
285 if (match) {
286 m_request.AddCompletion(cur_file_name);
287 }
288 }
289 return Searcher::eCallbackReturnContinue;
290 }
291
DoCompletion(SearchFilter * filter)292 void DoCompletion(SearchFilter *filter) override { filter->Search(*this); }
293
294 private:
295 const char *m_file_name;
296 const char *m_dir_name;
297
298 ModuleCompleter(const ModuleCompleter &) = delete;
299 const ModuleCompleter &operator=(const ModuleCompleter &) = delete;
300 };
301 } // namespace
302
SourceFiles(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)303 void CommandCompletions::SourceFiles(CommandInterpreter &interpreter,
304 CompletionRequest &request,
305 SearchFilter *searcher) {
306 SourceFileCompleter completer(interpreter, request);
307
308 if (searcher == nullptr) {
309 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
310 SearchFilterForUnconstrainedSearches null_searcher(target_sp);
311 completer.DoCompletion(&null_searcher);
312 } else {
313 completer.DoCompletion(searcher);
314 }
315 }
316
DiskFilesOrDirectories(const llvm::Twine & partial_name,bool only_directories,CompletionRequest & request,TildeExpressionResolver & Resolver)317 static void DiskFilesOrDirectories(const llvm::Twine &partial_name,
318 bool only_directories,
319 CompletionRequest &request,
320 TildeExpressionResolver &Resolver) {
321 llvm::SmallString<256> CompletionBuffer;
322 llvm::SmallString<256> Storage;
323 partial_name.toVector(CompletionBuffer);
324
325 if (CompletionBuffer.size() >= PATH_MAX)
326 return;
327
328 namespace path = llvm::sys::path;
329
330 llvm::StringRef SearchDir;
331 llvm::StringRef PartialItem;
332
333 if (CompletionBuffer.startswith("~")) {
334 llvm::StringRef Buffer(CompletionBuffer);
335 size_t FirstSep =
336 Buffer.find_if([](char c) { return path::is_separator(c); });
337
338 llvm::StringRef Username = Buffer.take_front(FirstSep);
339 llvm::StringRef Remainder;
340 if (FirstSep != llvm::StringRef::npos)
341 Remainder = Buffer.drop_front(FirstSep + 1);
342
343 llvm::SmallString<256> Resolved;
344 if (!Resolver.ResolveExact(Username, Resolved)) {
345 // We couldn't resolve it as a full username. If there were no slashes
346 // then this might be a partial username. We try to resolve it as such
347 // but after that, we're done regardless of any matches.
348 if (FirstSep == llvm::StringRef::npos) {
349 llvm::StringSet<> MatchSet;
350 Resolver.ResolvePartial(Username, MatchSet);
351 for (const auto &S : MatchSet) {
352 Resolved = S.getKey();
353 path::append(Resolved, path::get_separator());
354 request.AddCompletion(Resolved, "", CompletionMode::Partial);
355 }
356 }
357 return;
358 }
359
360 // If there was no trailing slash, then we're done as soon as we resolve
361 // the expression to the correct directory. Otherwise we need to continue
362 // looking for matches within that directory.
363 if (FirstSep == llvm::StringRef::npos) {
364 // Make sure it ends with a separator.
365 path::append(CompletionBuffer, path::get_separator());
366 request.AddCompletion(CompletionBuffer, "", CompletionMode::Partial);
367 return;
368 }
369
370 // We want to keep the form the user typed, so we special case this to
371 // search in the fully resolved directory, but CompletionBuffer keeps the
372 // unmodified form that the user typed.
373 Storage = Resolved;
374 llvm::StringRef RemainderDir = path::parent_path(Remainder);
375 if (!RemainderDir.empty()) {
376 // Append the remaining path to the resolved directory.
377 Storage.append(path::get_separator());
378 Storage.append(RemainderDir);
379 }
380 SearchDir = Storage;
381 } else {
382 SearchDir = path::parent_path(CompletionBuffer);
383 }
384
385 size_t FullPrefixLen = CompletionBuffer.size();
386
387 PartialItem = path::filename(CompletionBuffer);
388
389 // path::filename() will return "." when the passed path ends with a
390 // directory separator. We have to filter those out, but only when the
391 // "." doesn't come from the completion request itself.
392 if (PartialItem == "." && path::is_separator(CompletionBuffer.back()))
393 PartialItem = llvm::StringRef();
394
395 if (SearchDir.empty()) {
396 llvm::sys::fs::current_path(Storage);
397 SearchDir = Storage;
398 }
399 assert(!PartialItem.contains(path::get_separator()));
400
401 // SearchDir now contains the directory to search in, and Prefix contains the
402 // text we want to match against items in that directory.
403
404 FileSystem &fs = FileSystem::Instance();
405 std::error_code EC;
406 llvm::vfs::directory_iterator Iter = fs.DirBegin(SearchDir, EC);
407 llvm::vfs::directory_iterator End;
408 for (; Iter != End && !EC; Iter.increment(EC)) {
409 auto &Entry = *Iter;
410 llvm::ErrorOr<llvm::vfs::Status> Status = fs.GetStatus(Entry.path());
411
412 if (!Status)
413 continue;
414
415 auto Name = path::filename(Entry.path());
416
417 // Omit ".", ".."
418 if (Name == "." || Name == ".." || !Name.startswith(PartialItem))
419 continue;
420
421 bool is_dir = Status->isDirectory();
422
423 // If it's a symlink, then we treat it as a directory as long as the target
424 // is a directory.
425 if (Status->isSymlink()) {
426 FileSpec symlink_filespec(Entry.path());
427 FileSpec resolved_filespec;
428 auto error = fs.ResolveSymbolicLink(symlink_filespec, resolved_filespec);
429 if (error.Success())
430 is_dir = fs.IsDirectory(symlink_filespec);
431 }
432
433 if (only_directories && !is_dir)
434 continue;
435
436 // Shrink it back down so that it just has the original prefix the user
437 // typed and remove the part of the name which is common to the located
438 // item and what the user typed.
439 CompletionBuffer.resize(FullPrefixLen);
440 Name = Name.drop_front(PartialItem.size());
441 CompletionBuffer.append(Name);
442
443 if (is_dir) {
444 path::append(CompletionBuffer, path::get_separator());
445 }
446
447 CompletionMode mode =
448 is_dir ? CompletionMode::Partial : CompletionMode::Normal;
449 request.AddCompletion(CompletionBuffer, "", mode);
450 }
451 }
452
DiskFilesOrDirectories(const llvm::Twine & partial_name,bool only_directories,StringList & matches,TildeExpressionResolver & Resolver)453 static void DiskFilesOrDirectories(const llvm::Twine &partial_name,
454 bool only_directories, StringList &matches,
455 TildeExpressionResolver &Resolver) {
456 CompletionResult result;
457 std::string partial_name_str = partial_name.str();
458 CompletionRequest request(partial_name_str, partial_name_str.size(), result);
459 DiskFilesOrDirectories(partial_name, only_directories, request, Resolver);
460 result.GetMatches(matches);
461 }
462
DiskFilesOrDirectories(CompletionRequest & request,bool only_directories)463 static void DiskFilesOrDirectories(CompletionRequest &request,
464 bool only_directories) {
465 StandardTildeExpressionResolver resolver;
466 DiskFilesOrDirectories(request.GetCursorArgumentPrefix(), only_directories,
467 request, resolver);
468 }
469
DiskFiles(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)470 void CommandCompletions::DiskFiles(CommandInterpreter &interpreter,
471 CompletionRequest &request,
472 SearchFilter *searcher) {
473 DiskFilesOrDirectories(request, /*only_dirs*/ false);
474 }
475
DiskFiles(const llvm::Twine & partial_file_name,StringList & matches,TildeExpressionResolver & Resolver)476 void CommandCompletions::DiskFiles(const llvm::Twine &partial_file_name,
477 StringList &matches,
478 TildeExpressionResolver &Resolver) {
479 DiskFilesOrDirectories(partial_file_name, false, matches, Resolver);
480 }
481
DiskDirectories(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)482 void CommandCompletions::DiskDirectories(CommandInterpreter &interpreter,
483 CompletionRequest &request,
484 SearchFilter *searcher) {
485 DiskFilesOrDirectories(request, /*only_dirs*/ true);
486 }
487
DiskDirectories(const llvm::Twine & partial_file_name,StringList & matches,TildeExpressionResolver & Resolver)488 void CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name,
489 StringList &matches,
490 TildeExpressionResolver &Resolver) {
491 DiskFilesOrDirectories(partial_file_name, true, matches, Resolver);
492 }
493
RemoteDiskFiles(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)494 void CommandCompletions::RemoteDiskFiles(CommandInterpreter &interpreter,
495 CompletionRequest &request,
496 SearchFilter *searcher) {
497 lldb::PlatformSP platform_sp =
498 interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
499 if (platform_sp)
500 platform_sp->AutoCompleteDiskFileOrDirectory(request, false);
501 }
502
RemoteDiskDirectories(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)503 void CommandCompletions::RemoteDiskDirectories(CommandInterpreter &interpreter,
504 CompletionRequest &request,
505 SearchFilter *searcher) {
506 lldb::PlatformSP platform_sp =
507 interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
508 if (platform_sp)
509 platform_sp->AutoCompleteDiskFileOrDirectory(request, true);
510 }
511
Modules(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)512 void CommandCompletions::Modules(CommandInterpreter &interpreter,
513 CompletionRequest &request,
514 SearchFilter *searcher) {
515 ModuleCompleter completer(interpreter, request);
516
517 if (searcher == nullptr) {
518 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
519 SearchFilterForUnconstrainedSearches null_searcher(target_sp);
520 completer.DoCompletion(&null_searcher);
521 } else {
522 completer.DoCompletion(searcher);
523 }
524 }
525
ModuleUUIDs(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)526 void CommandCompletions::ModuleUUIDs(CommandInterpreter &interpreter,
527 CompletionRequest &request,
528 SearchFilter *searcher) {
529 const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();
530 if (!exe_ctx.HasTargetScope())
531 return;
532
533 exe_ctx.GetTargetPtr()->GetImages().ForEach(
534 [&request](const lldb::ModuleSP &module) {
535 StreamString strm;
536 module->GetDescription(strm.AsRawOstream(),
537 lldb::eDescriptionLevelInitial);
538 request.TryCompleteCurrentArg(module->GetUUID().GetAsString(),
539 strm.GetString());
540 return true;
541 });
542 }
543
Symbols(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)544 void CommandCompletions::Symbols(CommandInterpreter &interpreter,
545 CompletionRequest &request,
546 SearchFilter *searcher) {
547 SymbolCompleter completer(interpreter, request);
548
549 if (searcher == nullptr) {
550 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
551 SearchFilterForUnconstrainedSearches null_searcher(target_sp);
552 completer.DoCompletion(&null_searcher);
553 } else {
554 completer.DoCompletion(searcher);
555 }
556 }
557
SettingsNames(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)558 void CommandCompletions::SettingsNames(CommandInterpreter &interpreter,
559 CompletionRequest &request,
560 SearchFilter *searcher) {
561 // Cache the full setting name list
562 static StringList g_property_names;
563 if (g_property_names.GetSize() == 0) {
564 // Generate the full setting name list on demand
565 lldb::OptionValuePropertiesSP properties_sp(
566 interpreter.GetDebugger().GetValueProperties());
567 if (properties_sp) {
568 StreamString strm;
569 properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName);
570 const std::string &str = std::string(strm.GetString());
571 g_property_names.SplitIntoLines(str.c_str(), str.size());
572 }
573 }
574
575 for (const std::string &s : g_property_names)
576 request.TryCompleteCurrentArg(s);
577 }
578
PlatformPluginNames(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)579 void CommandCompletions::PlatformPluginNames(CommandInterpreter &interpreter,
580 CompletionRequest &request,
581 SearchFilter *searcher) {
582 PluginManager::AutoCompletePlatformName(request.GetCursorArgumentPrefix(),
583 request);
584 }
585
ArchitectureNames(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)586 void CommandCompletions::ArchitectureNames(CommandInterpreter &interpreter,
587 CompletionRequest &request,
588 SearchFilter *searcher) {
589 ArchSpec::AutoComplete(request);
590 }
591
VariablePath(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)592 void CommandCompletions::VariablePath(CommandInterpreter &interpreter,
593 CompletionRequest &request,
594 SearchFilter *searcher) {
595 Variable::AutoComplete(interpreter.GetExecutionContext(), request);
596 }
597
Registers(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)598 void CommandCompletions::Registers(CommandInterpreter &interpreter,
599 CompletionRequest &request,
600 SearchFilter *searcher) {
601 std::string reg_prefix = "";
602 if (request.GetCursorArgumentPrefix().startswith("$"))
603 reg_prefix = "$";
604
605 RegisterContext *reg_ctx =
606 interpreter.GetExecutionContext().GetRegisterContext();
607 const size_t reg_num = reg_ctx->GetRegisterCount();
608 for (size_t reg_idx = 0; reg_idx < reg_num; ++reg_idx) {
609 const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_idx);
610 request.TryCompleteCurrentArg(reg_prefix + reg_info->name,
611 reg_info->alt_name);
612 }
613 }
614
Breakpoints(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)615 void CommandCompletions::Breakpoints(CommandInterpreter &interpreter,
616 CompletionRequest &request,
617 SearchFilter *searcher) {
618 lldb::TargetSP target = interpreter.GetDebugger().GetSelectedTarget();
619 if (!target)
620 return;
621
622 const BreakpointList &breakpoints = target->GetBreakpointList();
623
624 std::unique_lock<std::recursive_mutex> lock;
625 target->GetBreakpointList().GetListMutex(lock);
626
627 size_t num_breakpoints = breakpoints.GetSize();
628 if (num_breakpoints == 0)
629 return;
630
631 for (size_t i = 0; i < num_breakpoints; ++i) {
632 lldb::BreakpointSP bp = breakpoints.GetBreakpointAtIndex(i);
633
634 StreamString s;
635 bp->GetDescription(&s, lldb::eDescriptionLevelBrief);
636 llvm::StringRef bp_info = s.GetString();
637
638 const size_t colon_pos = bp_info.find_first_of(':');
639 if (colon_pos != llvm::StringRef::npos)
640 bp_info = bp_info.drop_front(colon_pos + 2);
641
642 request.TryCompleteCurrentArg(std::to_string(bp->GetID()), bp_info);
643 }
644 }
645
BreakpointNames(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)646 void CommandCompletions::BreakpointNames(CommandInterpreter &interpreter,
647 CompletionRequest &request,
648 SearchFilter *searcher) {
649 lldb::TargetSP target = interpreter.GetDebugger().GetSelectedTarget();
650 if (!target)
651 return;
652
653 std::vector<std::string> name_list;
654 target->GetBreakpointNames(name_list);
655
656 for (const std::string &name : name_list)
657 request.TryCompleteCurrentArg(name);
658 }
659
ProcessPluginNames(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)660 void CommandCompletions::ProcessPluginNames(CommandInterpreter &interpreter,
661 CompletionRequest &request,
662 SearchFilter *searcher) {
663 PluginManager::AutoCompleteProcessName(request.GetCursorArgumentPrefix(),
664 request);
665 }
DisassemblyFlavors(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)666 void CommandCompletions::DisassemblyFlavors(CommandInterpreter &interpreter,
667 CompletionRequest &request,
668 SearchFilter *searcher) {
669 // Currently the only valid options for disassemble -F are default, and for
670 // Intel architectures, att and intel.
671 static const char *flavors[] = {"default", "att", "intel"};
672 for (const char *flavor : flavors) {
673 request.TryCompleteCurrentArg(flavor);
674 }
675 }
676
ProcessIDs(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)677 void CommandCompletions::ProcessIDs(CommandInterpreter &interpreter,
678 CompletionRequest &request,
679 SearchFilter *searcher) {
680 lldb::PlatformSP platform_sp(interpreter.GetPlatform(true));
681 if (!platform_sp)
682 return;
683 ProcessInstanceInfoList process_infos;
684 ProcessInstanceInfoMatch match_info;
685 platform_sp->FindProcesses(match_info, process_infos);
686 for (const ProcessInstanceInfo &info : process_infos)
687 request.TryCompleteCurrentArg(std::to_string(info.GetProcessID()),
688 info.GetNameAsStringRef());
689 }
690
ProcessNames(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)691 void CommandCompletions::ProcessNames(CommandInterpreter &interpreter,
692 CompletionRequest &request,
693 SearchFilter *searcher) {
694 lldb::PlatformSP platform_sp(interpreter.GetPlatform(true));
695 if (!platform_sp)
696 return;
697 ProcessInstanceInfoList process_infos;
698 ProcessInstanceInfoMatch match_info;
699 platform_sp->FindProcesses(match_info, process_infos);
700 for (const ProcessInstanceInfo &info : process_infos)
701 request.TryCompleteCurrentArg(info.GetNameAsStringRef());
702 }
703
TypeLanguages(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)704 void CommandCompletions::TypeLanguages(CommandInterpreter &interpreter,
705 CompletionRequest &request,
706 SearchFilter *searcher) {
707 for (int bit :
708 Language::GetLanguagesSupportingTypeSystems().bitvector.set_bits()) {
709 request.TryCompleteCurrentArg(
710 Language::GetNameForLanguageType(static_cast<lldb::LanguageType>(bit)));
711 }
712 }
713
FrameIndexes(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)714 void CommandCompletions::FrameIndexes(CommandInterpreter &interpreter,
715 CompletionRequest &request,
716 SearchFilter *searcher) {
717 const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();
718 if (!exe_ctx.HasProcessScope())
719 return;
720
721 lldb::ThreadSP thread_sp = exe_ctx.GetThreadSP();
722 const uint32_t frame_num = thread_sp->GetStackFrameCount();
723 for (uint32_t i = 0; i < frame_num; ++i) {
724 lldb::StackFrameSP frame_sp = thread_sp->GetStackFrameAtIndex(i);
725 StreamString strm;
726 frame_sp->Dump(&strm, false, true);
727 request.TryCompleteCurrentArg(std::to_string(i), strm.GetString());
728 }
729 }
730
StopHookIDs(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)731 void CommandCompletions::StopHookIDs(CommandInterpreter &interpreter,
732 CompletionRequest &request,
733 SearchFilter *searcher) {
734 const lldb::TargetSP target_sp =
735 interpreter.GetExecutionContext().GetTargetSP();
736 if (!target_sp)
737 return;
738
739 const size_t num = target_sp->GetNumStopHooks();
740 for (size_t idx = 0; idx < num; ++idx) {
741 StreamString strm;
742 // The value 11 is an offset to make the completion description looks
743 // neater.
744 strm.SetIndentLevel(11);
745 const Target::StopHookSP stophook_sp = target_sp->GetStopHookAtIndex(idx);
746 stophook_sp->GetDescription(&strm, lldb::eDescriptionLevelInitial);
747 request.TryCompleteCurrentArg(std::to_string(stophook_sp->GetID()),
748 strm.GetString());
749 }
750 }
751
ThreadIndexes(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)752 void CommandCompletions::ThreadIndexes(CommandInterpreter &interpreter,
753 CompletionRequest &request,
754 SearchFilter *searcher) {
755 const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();
756 if (!exe_ctx.HasProcessScope())
757 return;
758
759 ThreadList &threads = exe_ctx.GetProcessPtr()->GetThreadList();
760 lldb::ThreadSP thread_sp;
761 for (uint32_t idx = 0; (thread_sp = threads.GetThreadAtIndex(idx)); ++idx) {
762 StreamString strm;
763 thread_sp->GetStatus(strm, 0, 1, 1, true);
764 request.TryCompleteCurrentArg(std::to_string(thread_sp->GetIndexID()),
765 strm.GetString());
766 }
767 }
768
WatchPointIDs(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)769 void CommandCompletions::WatchPointIDs(CommandInterpreter &interpreter,
770 CompletionRequest &request,
771 SearchFilter *searcher) {
772 const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();
773 if (!exe_ctx.HasTargetScope())
774 return;
775
776 const WatchpointList &wp_list = exe_ctx.GetTargetPtr()->GetWatchpointList();
777 const size_t wp_num = wp_list.GetSize();
778 for (size_t idx = 0; idx < wp_num; ++idx) {
779 const lldb::WatchpointSP wp_sp = wp_list.GetByIndex(idx);
780 StreamString strm;
781 wp_sp->Dump(&strm);
782 request.TryCompleteCurrentArg(std::to_string(wp_sp->GetID()),
783 strm.GetString());
784 }
785 }
786
TypeCategoryNames(CommandInterpreter & interpreter,CompletionRequest & request,SearchFilter * searcher)787 void CommandCompletions::TypeCategoryNames(CommandInterpreter &interpreter,
788 CompletionRequest &request,
789 SearchFilter *searcher) {
790 DataVisualization::Categories::ForEach(
791 [&request](const lldb::TypeCategoryImplSP &category_sp) {
792 request.TryCompleteCurrentArg(category_sp->GetName(),
793 category_sp->GetDescription());
794 return true;
795 });
796 }
797