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 }