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 }