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