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