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/Core/FileSpecList.h"
13 #include "lldb/Core/Module.h"
14 #include "lldb/Core/PluginManager.h"
15 #include "lldb/Host/FileSystem.h"
16 #include "lldb/Interpreter/CommandCompletions.h"
17 #include "lldb/Interpreter/CommandInterpreter.h"
18 #include "lldb/Interpreter/OptionValueProperties.h"
19 #include "lldb/Symbol/CompileUnit.h"
20 #include "lldb/Symbol/Variable.h"
21 #include "lldb/Target/RegisterContext.h"
22 #include "lldb/Utility/FileSpec.h"
23 #include "lldb/Utility/StreamString.h"
24 #include "lldb/Utility/TildeExpressionResolver.h"
25 
26 #include "llvm/Support/FileSystem.h"
27 #include "llvm/Support/Path.h"
28 
29 using namespace lldb_private;
30 
31 // This is the command completion callback that is used to complete the
32 // argument of the option it is bound to (in the OptionDefinition table
33 // below).
34 typedef void (*CompletionCallback)(CommandInterpreter &interpreter,
35                                    CompletionRequest &request,
36                                    // A search filter to limit the search...
37                                    lldb_private::SearchFilter *searcher);
38 
39 struct CommonCompletionElement {
40   uint32_t type;
41   CompletionCallback callback;
42 };
43 
44 bool CommandCompletions::InvokeCommonCompletionCallbacks(
45     CommandInterpreter &interpreter, uint32_t completion_mask,
46     CompletionRequest &request, SearchFilter *searcher) {
47   bool handled = false;
48 
49   const CommonCompletionElement common_completions[] = {
50       {eSourceFileCompletion, CommandCompletions::SourceFiles},
51       {eDiskFileCompletion, CommandCompletions::DiskFiles},
52       {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories},
53       {eSymbolCompletion, CommandCompletions::Symbols},
54       {eModuleCompletion, CommandCompletions::Modules},
55       {eSettingsNameCompletion, CommandCompletions::SettingsNames},
56       {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames},
57       {eArchitectureCompletion, CommandCompletions::ArchitectureNames},
58       {eVariablePathCompletion, CommandCompletions::VariablePath},
59       {eRegisterCompletion, CommandCompletions::Registers},
60       {eBreakpointCompletion, CommandCompletions::Breakpoints},
61       {eProcessPluginCompletion, CommandCompletions::ProcessPluginNames},
62       {eNoCompletion, nullptr} // This one has to be last in the list.
63   };
64 
65   for (int i = 0;; i++) {
66     if (common_completions[i].type == eNoCompletion)
67       break;
68     else if ((common_completions[i].type & completion_mask) ==
69                  common_completions[i].type &&
70              common_completions[i].callback != nullptr) {
71       handled = true;
72       common_completions[i].callback(interpreter, request, searcher);
73     }
74   }
75   return handled;
76 }
77 
78 namespace {
79 // The Completer class is a convenient base class for building searchers that
80 // go along with the SearchFilter passed to the standard Completer functions.
81 class Completer : public Searcher {
82 public:
83   Completer(CommandInterpreter &interpreter, CompletionRequest &request)
84       : m_interpreter(interpreter), m_request(request) {}
85 
86   ~Completer() override = default;
87 
88   CallbackReturn SearchCallback(SearchFilter &filter, SymbolContext &context,
89                                 Address *addr) override = 0;
90 
91   lldb::SearchDepth GetDepth() override = 0;
92 
93   virtual void DoCompletion(SearchFilter *filter) = 0;
94 
95 protected:
96   CommandInterpreter &m_interpreter;
97   CompletionRequest &m_request;
98 
99 private:
100   DISALLOW_COPY_AND_ASSIGN(Completer);
101 };
102 } // namespace
103 
104 // SourceFileCompleter implements the source file completer
105 namespace {
106 class SourceFileCompleter : public Completer {
107 public:
108   SourceFileCompleter(CommandInterpreter &interpreter,
109                       CompletionRequest &request)
110       : Completer(interpreter, request), m_matching_files() {
111     FileSpec partial_spec(m_request.GetCursorArgumentPrefix());
112     m_file_name = partial_spec.GetFilename().GetCString();
113     m_dir_name = partial_spec.GetDirectory().GetCString();
114   }
115 
116   lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthCompUnit; }
117 
118   Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
119                                           SymbolContext &context,
120                                           Address *addr) override {
121     if (context.comp_unit != nullptr) {
122       const char *cur_file_name =
123           context.comp_unit->GetPrimaryFile().GetFilename().GetCString();
124       const char *cur_dir_name =
125           context.comp_unit->GetPrimaryFile().GetDirectory().GetCString();
126 
127       bool match = false;
128       if (m_file_name && cur_file_name &&
129           strstr(cur_file_name, m_file_name) == cur_file_name)
130         match = true;
131 
132       if (match && m_dir_name && cur_dir_name &&
133           strstr(cur_dir_name, m_dir_name) != cur_dir_name)
134         match = false;
135 
136       if (match) {
137         m_matching_files.AppendIfUnique(context.comp_unit->GetPrimaryFile());
138       }
139     }
140     return Searcher::eCallbackReturnContinue;
141   }
142 
143   void DoCompletion(SearchFilter *filter) override {
144     filter->Search(*this);
145     // Now convert the filelist to completions:
146     for (size_t i = 0; i < m_matching_files.GetSize(); i++) {
147       m_request.AddCompletion(
148           m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString());
149     }
150   }
151 
152 private:
153   FileSpecList m_matching_files;
154   const char *m_file_name;
155   const char *m_dir_name;
156 
157   DISALLOW_COPY_AND_ASSIGN(SourceFileCompleter);
158 };
159 } // namespace
160 
161 static bool regex_chars(const char comp) {
162   return llvm::StringRef("[](){}+.*|^$\\?").contains(comp);
163 }
164 
165 namespace {
166 class SymbolCompleter : public Completer {
167 
168 public:
169   SymbolCompleter(CommandInterpreter &interpreter, CompletionRequest &request)
170       : Completer(interpreter, request) {
171     std::string regex_str;
172     if (!m_request.GetCursorArgumentPrefix().empty()) {
173       regex_str.append("^");
174       regex_str.append(std::string(m_request.GetCursorArgumentPrefix()));
175     } else {
176       // Match anything since the completion string is empty
177       regex_str.append(".");
178     }
179     std::string::iterator pos =
180         find_if(regex_str.begin() + 1, regex_str.end(), regex_chars);
181     while (pos < regex_str.end()) {
182       pos = regex_str.insert(pos, '\\');
183       pos = find_if(pos + 2, regex_str.end(), regex_chars);
184     }
185     m_regex = RegularExpression(regex_str);
186   }
187 
188   lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }
189 
190   Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
191                                           SymbolContext &context,
192                                           Address *addr) override {
193     if (context.module_sp) {
194       SymbolContextList sc_list;
195       const bool include_symbols = true;
196       const bool include_inlines = true;
197       context.module_sp->FindFunctions(m_regex, include_symbols,
198                                        include_inlines, sc_list);
199 
200       SymbolContext sc;
201       // Now add the functions & symbols to the list - only add if unique:
202       for (uint32_t i = 0; i < sc_list.GetSize(); i++) {
203         if (sc_list.GetContextAtIndex(i, sc)) {
204           ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled);
205           // Ensure that the function name matches the regex. This is more than
206           // a sanity check. It is possible that the demangled function name
207           // does not start with the prefix, for example when it's in an
208           // anonymous namespace.
209           if (!func_name.IsEmpty() && m_regex.Execute(func_name.GetStringRef()))
210             m_match_set.insert(func_name);
211         }
212       }
213     }
214     return Searcher::eCallbackReturnContinue;
215   }
216 
217   void DoCompletion(SearchFilter *filter) override {
218     filter->Search(*this);
219     collection::iterator pos = m_match_set.begin(), end = m_match_set.end();
220     for (pos = m_match_set.begin(); pos != end; pos++)
221       m_request.AddCompletion((*pos).GetCString());
222   }
223 
224 private:
225   RegularExpression m_regex;
226   typedef std::set<ConstString> collection;
227   collection m_match_set;
228 
229   DISALLOW_COPY_AND_ASSIGN(SymbolCompleter);
230 };
231 } // namespace
232 
233 namespace {
234 class ModuleCompleter : public Completer {
235 public:
236   ModuleCompleter(CommandInterpreter &interpreter, CompletionRequest &request)
237       : Completer(interpreter, request) {
238     FileSpec partial_spec(m_request.GetCursorArgumentPrefix());
239     m_file_name = partial_spec.GetFilename().GetCString();
240     m_dir_name = partial_spec.GetDirectory().GetCString();
241   }
242 
243   lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }
244 
245   Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
246                                           SymbolContext &context,
247                                           Address *addr) override {
248     if (context.module_sp) {
249       const char *cur_file_name =
250           context.module_sp->GetFileSpec().GetFilename().GetCString();
251       const char *cur_dir_name =
252           context.module_sp->GetFileSpec().GetDirectory().GetCString();
253 
254       bool match = false;
255       if (m_file_name && cur_file_name &&
256           strstr(cur_file_name, m_file_name) == cur_file_name)
257         match = true;
258 
259       if (match && m_dir_name && cur_dir_name &&
260           strstr(cur_dir_name, m_dir_name) != cur_dir_name)
261         match = false;
262 
263       if (match) {
264         m_request.AddCompletion(cur_file_name);
265       }
266     }
267     return Searcher::eCallbackReturnContinue;
268   }
269 
270   void DoCompletion(SearchFilter *filter) override { filter->Search(*this); }
271 
272 private:
273   const char *m_file_name;
274   const char *m_dir_name;
275 
276   DISALLOW_COPY_AND_ASSIGN(ModuleCompleter);
277 };
278 } // namespace
279 
280 void CommandCompletions::SourceFiles(CommandInterpreter &interpreter,
281                                      CompletionRequest &request,
282                                      SearchFilter *searcher) {
283   SourceFileCompleter completer(interpreter, request);
284 
285   if (searcher == nullptr) {
286     lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
287     SearchFilterForUnconstrainedSearches null_searcher(target_sp);
288     completer.DoCompletion(&null_searcher);
289   } else {
290     completer.DoCompletion(searcher);
291   }
292 }
293 
294 static void DiskFilesOrDirectories(const llvm::Twine &partial_name,
295                                    bool only_directories,
296                                    CompletionRequest &request,
297                                    TildeExpressionResolver &Resolver) {
298   llvm::SmallString<256> CompletionBuffer;
299   llvm::SmallString<256> Storage;
300   partial_name.toVector(CompletionBuffer);
301 
302   if (CompletionBuffer.size() >= PATH_MAX)
303     return;
304 
305   namespace path = llvm::sys::path;
306 
307   llvm::StringRef SearchDir;
308   llvm::StringRef PartialItem;
309 
310   if (CompletionBuffer.startswith("~")) {
311     llvm::StringRef Buffer(CompletionBuffer);
312     size_t FirstSep =
313         Buffer.find_if([](char c) { return path::is_separator(c); });
314 
315     llvm::StringRef Username = Buffer.take_front(FirstSep);
316     llvm::StringRef Remainder;
317     if (FirstSep != llvm::StringRef::npos)
318       Remainder = Buffer.drop_front(FirstSep + 1);
319 
320     llvm::SmallString<256> Resolved;
321     if (!Resolver.ResolveExact(Username, Resolved)) {
322       // We couldn't resolve it as a full username.  If there were no slashes
323       // then this might be a partial username.   We try to resolve it as such
324       // but after that, we're done regardless of any matches.
325       if (FirstSep == llvm::StringRef::npos) {
326         llvm::StringSet<> MatchSet;
327         Resolver.ResolvePartial(Username, MatchSet);
328         for (const auto &S : MatchSet) {
329           Resolved = S.getKey();
330           path::append(Resolved, path::get_separator());
331           request.AddCompletion(Resolved, "", CompletionMode::Partial);
332         }
333       }
334       return;
335     }
336 
337     // If there was no trailing slash, then we're done as soon as we resolve
338     // the expression to the correct directory.  Otherwise we need to continue
339     // looking for matches within that directory.
340     if (FirstSep == llvm::StringRef::npos) {
341       // Make sure it ends with a separator.
342       path::append(CompletionBuffer, path::get_separator());
343       request.AddCompletion(CompletionBuffer, "", CompletionMode::Partial);
344       return;
345     }
346 
347     // We want to keep the form the user typed, so we special case this to
348     // search in the fully resolved directory, but CompletionBuffer keeps the
349     // unmodified form that the user typed.
350     Storage = Resolved;
351     llvm::StringRef RemainderDir = path::parent_path(Remainder);
352     if (!RemainderDir.empty()) {
353       // Append the remaining path to the resolved directory.
354       Storage.append(path::get_separator());
355       Storage.append(RemainderDir);
356     }
357     SearchDir = Storage;
358   } else {
359     SearchDir = path::parent_path(CompletionBuffer);
360   }
361 
362   size_t FullPrefixLen = CompletionBuffer.size();
363 
364   PartialItem = path::filename(CompletionBuffer);
365 
366   // path::filename() will return "." when the passed path ends with a
367   // directory separator. We have to filter those out, but only when the
368   // "." doesn't come from the completion request itself.
369   if (PartialItem == "." && path::is_separator(CompletionBuffer.back()))
370     PartialItem = llvm::StringRef();
371 
372   if (SearchDir.empty()) {
373     llvm::sys::fs::current_path(Storage);
374     SearchDir = Storage;
375   }
376   assert(!PartialItem.contains(path::get_separator()));
377 
378   // SearchDir now contains the directory to search in, and Prefix contains the
379   // text we want to match against items in that directory.
380 
381   FileSystem &fs = FileSystem::Instance();
382   std::error_code EC;
383   llvm::vfs::directory_iterator Iter = fs.DirBegin(SearchDir, EC);
384   llvm::vfs::directory_iterator End;
385   for (; Iter != End && !EC; Iter.increment(EC)) {
386     auto &Entry = *Iter;
387     llvm::ErrorOr<llvm::vfs::Status> Status = fs.GetStatus(Entry.path());
388 
389     if (!Status)
390       continue;
391 
392     auto Name = path::filename(Entry.path());
393 
394     // Omit ".", ".."
395     if (Name == "." || Name == ".." || !Name.startswith(PartialItem))
396       continue;
397 
398     bool is_dir = Status->isDirectory();
399 
400     // If it's a symlink, then we treat it as a directory as long as the target
401     // is a directory.
402     if (Status->isSymlink()) {
403       FileSpec symlink_filespec(Entry.path());
404       FileSpec resolved_filespec;
405       auto error = fs.ResolveSymbolicLink(symlink_filespec, resolved_filespec);
406       if (error.Success())
407         is_dir = fs.IsDirectory(symlink_filespec);
408     }
409 
410     if (only_directories && !is_dir)
411       continue;
412 
413     // Shrink it back down so that it just has the original prefix the user
414     // typed and remove the part of the name which is common to the located
415     // item and what the user typed.
416     CompletionBuffer.resize(FullPrefixLen);
417     Name = Name.drop_front(PartialItem.size());
418     CompletionBuffer.append(Name);
419 
420     if (is_dir) {
421       path::append(CompletionBuffer, path::get_separator());
422     }
423 
424     CompletionMode mode =
425         is_dir ? CompletionMode::Partial : CompletionMode::Normal;
426     request.AddCompletion(CompletionBuffer, "", mode);
427   }
428 }
429 
430 static void DiskFilesOrDirectories(const llvm::Twine &partial_name,
431                                    bool only_directories, StringList &matches,
432                                    TildeExpressionResolver &Resolver) {
433   CompletionResult result;
434   std::string partial_name_str = partial_name.str();
435   CompletionRequest request(partial_name_str, partial_name_str.size(), result);
436   DiskFilesOrDirectories(partial_name, only_directories, request, Resolver);
437   result.GetMatches(matches);
438 }
439 
440 static void DiskFilesOrDirectories(CompletionRequest &request,
441                                    bool only_directories) {
442   StandardTildeExpressionResolver resolver;
443   DiskFilesOrDirectories(request.GetCursorArgumentPrefix(), only_directories,
444                          request, resolver);
445 }
446 
447 void CommandCompletions::DiskFiles(CommandInterpreter &interpreter,
448                                    CompletionRequest &request,
449                                    SearchFilter *searcher) {
450   DiskFilesOrDirectories(request, /*only_dirs*/ false);
451 }
452 
453 void CommandCompletions::DiskFiles(const llvm::Twine &partial_file_name,
454                                    StringList &matches,
455                                    TildeExpressionResolver &Resolver) {
456   DiskFilesOrDirectories(partial_file_name, false, matches, Resolver);
457 }
458 
459 void CommandCompletions::DiskDirectories(CommandInterpreter &interpreter,
460                                          CompletionRequest &request,
461                                          SearchFilter *searcher) {
462   DiskFilesOrDirectories(request, /*only_dirs*/ true);
463 }
464 
465 void CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name,
466                                          StringList &matches,
467                                          TildeExpressionResolver &Resolver) {
468   DiskFilesOrDirectories(partial_file_name, true, matches, Resolver);
469 }
470 
471 void CommandCompletions::Modules(CommandInterpreter &interpreter,
472                                  CompletionRequest &request,
473                                  SearchFilter *searcher) {
474   ModuleCompleter completer(interpreter, request);
475 
476   if (searcher == nullptr) {
477     lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
478     SearchFilterForUnconstrainedSearches null_searcher(target_sp);
479     completer.DoCompletion(&null_searcher);
480   } else {
481     completer.DoCompletion(searcher);
482   }
483 }
484 
485 void CommandCompletions::Symbols(CommandInterpreter &interpreter,
486                                  CompletionRequest &request,
487                                  SearchFilter *searcher) {
488   SymbolCompleter completer(interpreter, request);
489 
490   if (searcher == nullptr) {
491     lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
492     SearchFilterForUnconstrainedSearches null_searcher(target_sp);
493     completer.DoCompletion(&null_searcher);
494   } else {
495     completer.DoCompletion(searcher);
496   }
497 }
498 
499 void CommandCompletions::SettingsNames(CommandInterpreter &interpreter,
500                                        CompletionRequest &request,
501                                        SearchFilter *searcher) {
502   // Cache the full setting name list
503   static StringList g_property_names;
504   if (g_property_names.GetSize() == 0) {
505     // Generate the full setting name list on demand
506     lldb::OptionValuePropertiesSP properties_sp(
507         interpreter.GetDebugger().GetValueProperties());
508     if (properties_sp) {
509       StreamString strm;
510       properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName);
511       const std::string &str = std::string(strm.GetString());
512       g_property_names.SplitIntoLines(str.c_str(), str.size());
513     }
514   }
515 
516   for (const std::string &s : g_property_names)
517     request.TryCompleteCurrentArg(s);
518 }
519 
520 void CommandCompletions::PlatformPluginNames(CommandInterpreter &interpreter,
521                                              CompletionRequest &request,
522                                              SearchFilter *searcher) {
523   PluginManager::AutoCompletePlatformName(request.GetCursorArgumentPrefix(),
524                                           request);
525 }
526 
527 void CommandCompletions::ArchitectureNames(CommandInterpreter &interpreter,
528                                            CompletionRequest &request,
529                                            SearchFilter *searcher) {
530   ArchSpec::AutoComplete(request);
531 }
532 
533 void CommandCompletions::VariablePath(CommandInterpreter &interpreter,
534                                       CompletionRequest &request,
535                                       SearchFilter *searcher) {
536   Variable::AutoComplete(interpreter.GetExecutionContext(), request);
537 }
538 
539 void CommandCompletions::Registers(CommandInterpreter &interpreter,
540                                    CompletionRequest &request,
541                                    SearchFilter *searcher) {
542   std::string reg_prefix = "";
543   if (request.GetCursorArgumentPrefix().startswith("$"))
544     reg_prefix = "$";
545 
546   RegisterContext *reg_ctx =
547       interpreter.GetExecutionContext().GetRegisterContext();
548   const size_t reg_num = reg_ctx->GetRegisterCount();
549   for (size_t reg_idx = 0; reg_idx < reg_num; ++reg_idx) {
550     const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_idx);
551     request.TryCompleteCurrentArg(reg_prefix + reg_info->name,
552                                   reg_info->alt_name);
553   }
554 }
555 
556 void CommandCompletions::Breakpoints(CommandInterpreter &interpreter,
557                                      CompletionRequest &request,
558                                      SearchFilter *searcher) {
559   lldb::TargetSP target = interpreter.GetDebugger().GetSelectedTarget();
560   if (!target)
561     return;
562 
563   const BreakpointList &breakpoints = target->GetBreakpointList();
564 
565   std::unique_lock<std::recursive_mutex> lock;
566   target->GetBreakpointList().GetListMutex(lock);
567 
568   size_t num_breakpoints = breakpoints.GetSize();
569   if (num_breakpoints == 0)
570     return;
571 
572   for (size_t i = 0; i < num_breakpoints; ++i) {
573     lldb::BreakpointSP bp = breakpoints.GetBreakpointAtIndex(i);
574 
575     StreamString s;
576     bp->GetDescription(&s, lldb::eDescriptionLevelBrief);
577     llvm::StringRef bp_info = s.GetString();
578 
579     const size_t colon_pos = bp_info.find_first_of(':');
580     if (colon_pos != llvm::StringRef::npos)
581       bp_info = bp_info.drop_front(colon_pos + 2);
582 
583     request.TryCompleteCurrentArg(std::to_string(bp->GetID()), bp_info);
584   }
585 }
586 
587 void CommandCompletions::ProcessPluginNames(CommandInterpreter &interpreter,
588                                             CompletionRequest &request,
589                                             SearchFilter *searcher) {
590   PluginManager::AutoCompleteProcessName(request.GetCursorArgumentPrefix(),
591                                          request);
592 }