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