1 //===-- CommandObjectSource.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 "CommandObjectSource.h" 10 11 #include "lldb/Core/Debugger.h" 12 #include "lldb/Core/FileLineResolver.h" 13 #include "lldb/Core/Module.h" 14 #include "lldb/Core/ModuleSpec.h" 15 #include "lldb/Core/SourceManager.h" 16 #include "lldb/Host/OptionParser.h" 17 #include "lldb/Interpreter/CommandOptionArgumentTable.h" 18 #include "lldb/Interpreter/CommandReturnObject.h" 19 #include "lldb/Interpreter/OptionArgParser.h" 20 #include "lldb/Interpreter/OptionValueFileColonLine.h" 21 #include "lldb/Interpreter/Options.h" 22 #include "lldb/Symbol/CompileUnit.h" 23 #include "lldb/Symbol/Function.h" 24 #include "lldb/Symbol/Symbol.h" 25 #include "lldb/Target/SectionLoadList.h" 26 #include "lldb/Target/StackFrame.h" 27 #include "lldb/Utility/FileSpec.h" 28 #include <optional> 29 30 using namespace lldb; 31 using namespace lldb_private; 32 33 #pragma mark CommandObjectSourceInfo 34 // CommandObjectSourceInfo - debug line entries dumping command 35 #define LLDB_OPTIONS_source_info 36 #include "CommandOptions.inc" 37 38 class CommandObjectSourceInfo : public CommandObjectParsed { 39 class CommandOptions : public Options { 40 public: 41 CommandOptions() = default; 42 43 ~CommandOptions() override = default; 44 45 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 46 ExecutionContext *execution_context) override { 47 Status error; 48 const int short_option = GetDefinitions()[option_idx].short_option; 49 switch (short_option) { 50 case 'l': 51 if (option_arg.getAsInteger(0, start_line)) 52 error.SetErrorStringWithFormat("invalid line number: '%s'", 53 option_arg.str().c_str()); 54 break; 55 56 case 'e': 57 if (option_arg.getAsInteger(0, end_line)) 58 error.SetErrorStringWithFormat("invalid line number: '%s'", 59 option_arg.str().c_str()); 60 break; 61 62 case 'c': 63 if (option_arg.getAsInteger(0, num_lines)) 64 error.SetErrorStringWithFormat("invalid line count: '%s'", 65 option_arg.str().c_str()); 66 break; 67 68 case 'f': 69 file_name = std::string(option_arg); 70 break; 71 72 case 'n': 73 symbol_name = std::string(option_arg); 74 break; 75 76 case 'a': { 77 address = OptionArgParser::ToAddress(execution_context, option_arg, 78 LLDB_INVALID_ADDRESS, &error); 79 } break; 80 case 's': 81 modules.push_back(std::string(option_arg)); 82 break; 83 default: 84 llvm_unreachable("Unimplemented option"); 85 } 86 87 return error; 88 } 89 90 void OptionParsingStarting(ExecutionContext *execution_context) override { 91 file_spec.Clear(); 92 file_name.clear(); 93 symbol_name.clear(); 94 address = LLDB_INVALID_ADDRESS; 95 start_line = 0; 96 end_line = 0; 97 num_lines = 0; 98 modules.clear(); 99 } 100 101 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 102 return llvm::ArrayRef(g_source_info_options); 103 } 104 105 // Instance variables to hold the values for command options. 106 FileSpec file_spec; 107 std::string file_name; 108 std::string symbol_name; 109 lldb::addr_t address; 110 uint32_t start_line; 111 uint32_t end_line; 112 uint32_t num_lines; 113 std::vector<std::string> modules; 114 }; 115 116 public: 117 CommandObjectSourceInfo(CommandInterpreter &interpreter) 118 : CommandObjectParsed( 119 interpreter, "source info", 120 "Display source line information for the current target " 121 "process. Defaults to instruction pointer in current stack " 122 "frame.", 123 nullptr, eCommandRequiresTarget) {} 124 125 ~CommandObjectSourceInfo() override = default; 126 127 Options *GetOptions() override { return &m_options; } 128 129 protected: 130 // Dump the line entries in each symbol context. Return the number of entries 131 // found. If module_list is set, only dump lines contained in one of the 132 // modules. If file_spec is set, only dump lines in the file. If the 133 // start_line option was specified, don't print lines less than start_line. 134 // If the end_line option was specified, don't print lines greater than 135 // end_line. If the num_lines option was specified, dont print more than 136 // num_lines entries. 137 uint32_t DumpLinesInSymbolContexts(Stream &strm, 138 const SymbolContextList &sc_list, 139 const ModuleList &module_list, 140 const FileSpec &file_spec) { 141 uint32_t start_line = m_options.start_line; 142 uint32_t end_line = m_options.end_line; 143 uint32_t num_lines = m_options.num_lines; 144 Target *target = m_exe_ctx.GetTargetPtr(); 145 146 uint32_t num_matches = 0; 147 // Dump all the line entries for the file in the list. 148 ConstString last_module_file_name; 149 for (const SymbolContext &sc : sc_list) { 150 if (sc.comp_unit) { 151 Module *module = sc.module_sp.get(); 152 CompileUnit *cu = sc.comp_unit; 153 const LineEntry &line_entry = sc.line_entry; 154 assert(module && cu); 155 156 // Are we looking for specific modules, files or lines? 157 if (module_list.GetSize() && 158 module_list.GetIndexForModule(module) == LLDB_INVALID_INDEX32) 159 continue; 160 if (!FileSpec::Match(file_spec, line_entry.file)) 161 continue; 162 if (start_line > 0 && line_entry.line < start_line) 163 continue; 164 if (end_line > 0 && line_entry.line > end_line) 165 continue; 166 if (num_lines > 0 && num_matches > num_lines) 167 continue; 168 169 // Print a new header if the module changed. 170 ConstString module_file_name = module->GetFileSpec().GetFilename(); 171 assert(module_file_name); 172 if (module_file_name != last_module_file_name) { 173 if (num_matches > 0) 174 strm << "\n\n"; 175 strm << "Lines found in module `" << module_file_name << "\n"; 176 } 177 // Dump the line entry. 178 line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu, 179 target, /*show_address_only=*/false); 180 strm << "\n"; 181 last_module_file_name = module_file_name; 182 num_matches++; 183 } 184 } 185 return num_matches; 186 } 187 188 // Dump the requested line entries for the file in the compilation unit. 189 // Return the number of entries found. If module_list is set, only dump lines 190 // contained in one of the modules. If the start_line option was specified, 191 // don't print lines less than start_line. If the end_line option was 192 // specified, don't print lines greater than end_line. If the num_lines 193 // option was specified, dont print more than num_lines entries. 194 uint32_t DumpFileLinesInCompUnit(Stream &strm, Module *module, 195 CompileUnit *cu, const FileSpec &file_spec) { 196 uint32_t start_line = m_options.start_line; 197 uint32_t end_line = m_options.end_line; 198 uint32_t num_lines = m_options.num_lines; 199 Target *target = m_exe_ctx.GetTargetPtr(); 200 201 uint32_t num_matches = 0; 202 assert(module); 203 if (cu) { 204 assert(file_spec.GetFilename().AsCString()); 205 bool has_path = (file_spec.GetDirectory().AsCString() != nullptr); 206 const FileSpecList &cu_file_list = cu->GetSupportFiles(); 207 size_t file_idx = cu_file_list.FindFileIndex(0, file_spec, has_path); 208 if (file_idx != UINT32_MAX) { 209 // Update the file to how it appears in the CU. 210 const FileSpec &cu_file_spec = 211 cu_file_list.GetFileSpecAtIndex(file_idx); 212 213 // Dump all matching lines at or above start_line for the file in the 214 // CU. 215 ConstString file_spec_name = file_spec.GetFilename(); 216 ConstString module_file_name = module->GetFileSpec().GetFilename(); 217 bool cu_header_printed = false; 218 uint32_t line = start_line; 219 while (true) { 220 LineEntry line_entry; 221 222 // Find the lowest index of a line entry with a line equal to or 223 // higher than 'line'. 224 uint32_t start_idx = 0; 225 start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec, 226 /*exact=*/false, &line_entry); 227 if (start_idx == UINT32_MAX) 228 // No more line entries for our file in this CU. 229 break; 230 231 if (end_line > 0 && line_entry.line > end_line) 232 break; 233 234 // Loop through to find any other entries for this line, dumping 235 // each. 236 line = line_entry.line; 237 do { 238 num_matches++; 239 if (num_lines > 0 && num_matches > num_lines) 240 break; 241 assert(cu_file_spec == line_entry.file); 242 if (!cu_header_printed) { 243 if (num_matches > 0) 244 strm << "\n\n"; 245 strm << "Lines found for file " << file_spec_name 246 << " in compilation unit " 247 << cu->GetPrimaryFile().GetFilename() << " in `" 248 << module_file_name << "\n"; 249 cu_header_printed = true; 250 } 251 line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu, 252 target, /*show_address_only=*/false); 253 strm << "\n"; 254 255 // Anymore after this one? 256 start_idx++; 257 start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec, 258 /*exact=*/true, &line_entry); 259 } while (start_idx != UINT32_MAX); 260 261 // Try the next higher line, starting over at start_idx 0. 262 line++; 263 } 264 } 265 } 266 return num_matches; 267 } 268 269 // Dump the requested line entries for the file in the module. Return the 270 // number of entries found. If module_list is set, only dump lines contained 271 // in one of the modules. If the start_line option was specified, don't print 272 // lines less than start_line. If the end_line option was specified, don't 273 // print lines greater than end_line. If the num_lines option was specified, 274 // dont print more than num_lines entries. 275 uint32_t DumpFileLinesInModule(Stream &strm, Module *module, 276 const FileSpec &file_spec) { 277 uint32_t num_matches = 0; 278 if (module) { 279 // Look through all the compilation units (CUs) in this module for ones 280 // that contain lines of code from this source file. 281 for (size_t i = 0; i < module->GetNumCompileUnits(); i++) { 282 // Look for a matching source file in this CU. 283 CompUnitSP cu_sp(module->GetCompileUnitAtIndex(i)); 284 if (cu_sp) { 285 num_matches += 286 DumpFileLinesInCompUnit(strm, module, cu_sp.get(), file_spec); 287 } 288 } 289 } 290 return num_matches; 291 } 292 293 // Given an address and a list of modules, append the symbol contexts of all 294 // line entries containing the address found in the modules and return the 295 // count of matches. If none is found, return an error in 'error_strm'. 296 size_t GetSymbolContextsForAddress(const ModuleList &module_list, 297 lldb::addr_t addr, 298 SymbolContextList &sc_list, 299 StreamString &error_strm) { 300 Address so_addr; 301 size_t num_matches = 0; 302 assert(module_list.GetSize() > 0); 303 Target *target = m_exe_ctx.GetTargetPtr(); 304 if (target->GetSectionLoadList().IsEmpty()) { 305 // The target isn't loaded yet, we need to lookup the file address in all 306 // modules. Note: the module list option does not apply to addresses. 307 const size_t num_modules = module_list.GetSize(); 308 for (size_t i = 0; i < num_modules; ++i) { 309 ModuleSP module_sp(module_list.GetModuleAtIndex(i)); 310 if (!module_sp) 311 continue; 312 if (module_sp->ResolveFileAddress(addr, so_addr)) { 313 SymbolContext sc; 314 sc.Clear(true); 315 if (module_sp->ResolveSymbolContextForAddress( 316 so_addr, eSymbolContextEverything, sc) & 317 eSymbolContextLineEntry) { 318 sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false); 319 ++num_matches; 320 } 321 } 322 } 323 if (num_matches == 0) 324 error_strm.Printf("Source information for file address 0x%" PRIx64 325 " not found in any modules.\n", 326 addr); 327 } else { 328 // The target has some things loaded, resolve this address to a compile 329 // unit + file + line and display 330 if (target->GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) { 331 ModuleSP module_sp(so_addr.GetModule()); 332 // Check to make sure this module is in our list. 333 if (module_sp && module_list.GetIndexForModule(module_sp.get()) != 334 LLDB_INVALID_INDEX32) { 335 SymbolContext sc; 336 sc.Clear(true); 337 if (module_sp->ResolveSymbolContextForAddress( 338 so_addr, eSymbolContextEverything, sc) & 339 eSymbolContextLineEntry) { 340 sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false); 341 ++num_matches; 342 } else { 343 StreamString addr_strm; 344 so_addr.Dump(&addr_strm, nullptr, 345 Address::DumpStyleModuleWithFileAddress); 346 error_strm.Printf( 347 "Address 0x%" PRIx64 " resolves to %s, but there is" 348 " no source information available for this address.\n", 349 addr, addr_strm.GetData()); 350 } 351 } else { 352 StreamString addr_strm; 353 so_addr.Dump(&addr_strm, nullptr, 354 Address::DumpStyleModuleWithFileAddress); 355 error_strm.Printf("Address 0x%" PRIx64 356 " resolves to %s, but it cannot" 357 " be found in any modules.\n", 358 addr, addr_strm.GetData()); 359 } 360 } else 361 error_strm.Printf("Unable to resolve address 0x%" PRIx64 ".\n", addr); 362 } 363 return num_matches; 364 } 365 366 // Dump the line entries found in functions matching the name specified in 367 // the option. 368 bool DumpLinesInFunctions(CommandReturnObject &result) { 369 SymbolContextList sc_list_funcs; 370 ConstString name(m_options.symbol_name.c_str()); 371 SymbolContextList sc_list_lines; 372 Target *target = m_exe_ctx.GetTargetPtr(); 373 uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); 374 375 ModuleFunctionSearchOptions function_options; 376 function_options.include_symbols = false; 377 function_options.include_inlines = true; 378 379 // Note: module_list can't be const& because FindFunctionSymbols isn't 380 // const. 381 ModuleList module_list = 382 (m_module_list.GetSize() > 0) ? m_module_list : target->GetImages(); 383 module_list.FindFunctions(name, eFunctionNameTypeAuto, function_options, 384 sc_list_funcs); 385 size_t num_matches = sc_list_funcs.GetSize(); 386 387 if (!num_matches) { 388 // If we didn't find any functions with that name, try searching for 389 // symbols that line up exactly with function addresses. 390 SymbolContextList sc_list_symbols; 391 module_list.FindFunctionSymbols(name, eFunctionNameTypeAuto, 392 sc_list_symbols); 393 for (const SymbolContext &sc : sc_list_symbols) { 394 if (sc.symbol && sc.symbol->ValueIsAddress()) { 395 const Address &base_address = sc.symbol->GetAddressRef(); 396 Function *function = base_address.CalculateSymbolContextFunction(); 397 if (function) { 398 sc_list_funcs.Append(SymbolContext(function)); 399 num_matches++; 400 } 401 } 402 } 403 } 404 if (num_matches == 0) { 405 result.AppendErrorWithFormat("Could not find function named \'%s\'.\n", 406 m_options.symbol_name.c_str()); 407 return false; 408 } 409 for (const SymbolContext &sc : sc_list_funcs) { 410 bool context_found_for_symbol = false; 411 // Loop through all the ranges in the function. 412 AddressRange range; 413 for (uint32_t r = 0; 414 sc.GetAddressRange(eSymbolContextEverything, r, 415 /*use_inline_block_range=*/true, range); 416 ++r) { 417 // Append the symbol contexts for each address in the range to 418 // sc_list_lines. 419 const Address &base_address = range.GetBaseAddress(); 420 const addr_t size = range.GetByteSize(); 421 lldb::addr_t start_addr = base_address.GetLoadAddress(target); 422 if (start_addr == LLDB_INVALID_ADDRESS) 423 start_addr = base_address.GetFileAddress(); 424 lldb::addr_t end_addr = start_addr + size; 425 for (lldb::addr_t addr = start_addr; addr < end_addr; 426 addr += addr_byte_size) { 427 StreamString error_strm; 428 if (!GetSymbolContextsForAddress(module_list, addr, sc_list_lines, 429 error_strm)) 430 result.AppendWarningWithFormat("in symbol '%s': %s", 431 sc.GetFunctionName().AsCString(), 432 error_strm.GetData()); 433 else 434 context_found_for_symbol = true; 435 } 436 } 437 if (!context_found_for_symbol) 438 result.AppendWarningWithFormat("Unable to find line information" 439 " for matching symbol '%s'.\n", 440 sc.GetFunctionName().AsCString()); 441 } 442 if (sc_list_lines.GetSize() == 0) { 443 result.AppendErrorWithFormat("No line information could be found" 444 " for any symbols matching '%s'.\n", 445 name.AsCString()); 446 return false; 447 } 448 FileSpec file_spec; 449 if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list_lines, 450 module_list, file_spec)) { 451 result.AppendErrorWithFormat( 452 "Unable to dump line information for symbol '%s'.\n", 453 name.AsCString()); 454 return false; 455 } 456 return true; 457 } 458 459 // Dump the line entries found for the address specified in the option. 460 bool DumpLinesForAddress(CommandReturnObject &result) { 461 Target *target = m_exe_ctx.GetTargetPtr(); 462 SymbolContextList sc_list; 463 464 StreamString error_strm; 465 if (!GetSymbolContextsForAddress(target->GetImages(), m_options.address, 466 sc_list, error_strm)) { 467 result.AppendErrorWithFormat("%s.\n", error_strm.GetData()); 468 return false; 469 } 470 ModuleList module_list; 471 FileSpec file_spec; 472 if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list, 473 module_list, file_spec)) { 474 result.AppendErrorWithFormat("No modules contain load address 0x%" PRIx64 475 ".\n", 476 m_options.address); 477 return false; 478 } 479 return true; 480 } 481 482 // Dump the line entries found in the file specified in the option. 483 bool DumpLinesForFile(CommandReturnObject &result) { 484 FileSpec file_spec(m_options.file_name); 485 const char *filename = m_options.file_name.c_str(); 486 Target *target = m_exe_ctx.GetTargetPtr(); 487 const ModuleList &module_list = 488 (m_module_list.GetSize() > 0) ? m_module_list : target->GetImages(); 489 490 bool displayed_something = false; 491 const size_t num_modules = module_list.GetSize(); 492 for (uint32_t i = 0; i < num_modules; ++i) { 493 // Dump lines for this module. 494 Module *module = module_list.GetModulePointerAtIndex(i); 495 assert(module); 496 if (DumpFileLinesInModule(result.GetOutputStream(), module, file_spec)) 497 displayed_something = true; 498 } 499 if (!displayed_something) { 500 result.AppendErrorWithFormat("No source filenames matched '%s'.\n", 501 filename); 502 return false; 503 } 504 return true; 505 } 506 507 // Dump the line entries for the current frame. 508 bool DumpLinesForFrame(CommandReturnObject &result) { 509 StackFrame *cur_frame = m_exe_ctx.GetFramePtr(); 510 if (cur_frame == nullptr) { 511 result.AppendError( 512 "No selected frame to use to find the default source."); 513 return false; 514 } else if (!cur_frame->HasDebugInformation()) { 515 result.AppendError("No debug info for the selected frame."); 516 return false; 517 } else { 518 const SymbolContext &sc = 519 cur_frame->GetSymbolContext(eSymbolContextLineEntry); 520 SymbolContextList sc_list; 521 sc_list.Append(sc); 522 ModuleList module_list; 523 FileSpec file_spec; 524 if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list, 525 module_list, file_spec)) { 526 result.AppendError( 527 "No source line info available for the selected frame."); 528 return false; 529 } 530 } 531 return true; 532 } 533 534 bool DoExecute(Args &command, CommandReturnObject &result) override { 535 Target *target = m_exe_ctx.GetTargetPtr(); 536 if (target == nullptr) { 537 target = GetDebugger().GetSelectedTarget().get(); 538 if (target == nullptr) { 539 result.AppendError("invalid target, create a debug target using the " 540 "'target create' command."); 541 return false; 542 } 543 } 544 545 uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); 546 result.GetOutputStream().SetAddressByteSize(addr_byte_size); 547 result.GetErrorStream().SetAddressByteSize(addr_byte_size); 548 549 // Collect the list of modules to search. 550 m_module_list.Clear(); 551 if (!m_options.modules.empty()) { 552 for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) { 553 FileSpec module_file_spec(m_options.modules[i]); 554 if (module_file_spec) { 555 ModuleSpec module_spec(module_file_spec); 556 target->GetImages().FindModules(module_spec, m_module_list); 557 if (m_module_list.IsEmpty()) 558 result.AppendWarningWithFormat("No module found for '%s'.\n", 559 m_options.modules[i].c_str()); 560 } 561 } 562 if (!m_module_list.GetSize()) { 563 result.AppendError("No modules match the input."); 564 return false; 565 } 566 } else if (target->GetImages().GetSize() == 0) { 567 result.AppendError("The target has no associated executable images."); 568 return false; 569 } 570 571 // Check the arguments to see what lines we should dump. 572 if (!m_options.symbol_name.empty()) { 573 // Print lines for symbol. 574 if (DumpLinesInFunctions(result)) 575 result.SetStatus(eReturnStatusSuccessFinishResult); 576 else 577 result.SetStatus(eReturnStatusFailed); 578 } else if (m_options.address != LLDB_INVALID_ADDRESS) { 579 // Print lines for an address. 580 if (DumpLinesForAddress(result)) 581 result.SetStatus(eReturnStatusSuccessFinishResult); 582 else 583 result.SetStatus(eReturnStatusFailed); 584 } else if (!m_options.file_name.empty()) { 585 // Dump lines for a file. 586 if (DumpLinesForFile(result)) 587 result.SetStatus(eReturnStatusSuccessFinishResult); 588 else 589 result.SetStatus(eReturnStatusFailed); 590 } else { 591 // Dump the line for the current frame. 592 if (DumpLinesForFrame(result)) 593 result.SetStatus(eReturnStatusSuccessFinishResult); 594 else 595 result.SetStatus(eReturnStatusFailed); 596 } 597 return result.Succeeded(); 598 } 599 600 CommandOptions m_options; 601 ModuleList m_module_list; 602 }; 603 604 #pragma mark CommandObjectSourceList 605 // CommandObjectSourceList 606 #define LLDB_OPTIONS_source_list 607 #include "CommandOptions.inc" 608 609 class CommandObjectSourceList : public CommandObjectParsed { 610 class CommandOptions : public Options { 611 public: 612 CommandOptions() = default; 613 614 ~CommandOptions() override = default; 615 616 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 617 ExecutionContext *execution_context) override { 618 Status error; 619 const int short_option = GetDefinitions()[option_idx].short_option; 620 switch (short_option) { 621 case 'l': 622 if (option_arg.getAsInteger(0, start_line)) 623 error.SetErrorStringWithFormat("invalid line number: '%s'", 624 option_arg.str().c_str()); 625 break; 626 627 case 'c': 628 if (option_arg.getAsInteger(0, num_lines)) 629 error.SetErrorStringWithFormat("invalid line count: '%s'", 630 option_arg.str().c_str()); 631 break; 632 633 case 'f': 634 file_name = std::string(option_arg); 635 break; 636 637 case 'n': 638 symbol_name = std::string(option_arg); 639 break; 640 641 case 'a': { 642 address = OptionArgParser::ToAddress(execution_context, option_arg, 643 LLDB_INVALID_ADDRESS, &error); 644 } break; 645 case 's': 646 modules.push_back(std::string(option_arg)); 647 break; 648 649 case 'b': 650 show_bp_locs = true; 651 break; 652 case 'r': 653 reverse = true; 654 break; 655 case 'y': 656 { 657 OptionValueFileColonLine value; 658 Status fcl_err = value.SetValueFromString(option_arg); 659 if (!fcl_err.Success()) { 660 error.SetErrorStringWithFormat( 661 "Invalid value for file:line specifier: %s", 662 fcl_err.AsCString()); 663 } else { 664 file_name = value.GetFileSpec().GetPath(); 665 start_line = value.GetLineNumber(); 666 // I don't see anything useful to do with a column number, but I don't 667 // want to complain since someone may well have cut and pasted a 668 // listing from somewhere that included a column. 669 } 670 } break; 671 default: 672 llvm_unreachable("Unimplemented option"); 673 } 674 675 return error; 676 } 677 678 void OptionParsingStarting(ExecutionContext *execution_context) override { 679 file_spec.Clear(); 680 file_name.clear(); 681 symbol_name.clear(); 682 address = LLDB_INVALID_ADDRESS; 683 start_line = 0; 684 num_lines = 0; 685 show_bp_locs = false; 686 reverse = false; 687 modules.clear(); 688 } 689 690 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 691 return llvm::ArrayRef(g_source_list_options); 692 } 693 694 // Instance variables to hold the values for command options. 695 FileSpec file_spec; 696 std::string file_name; 697 std::string symbol_name; 698 lldb::addr_t address; 699 uint32_t start_line; 700 uint32_t num_lines; 701 std::vector<std::string> modules; 702 bool show_bp_locs; 703 bool reverse; 704 }; 705 706 public: 707 CommandObjectSourceList(CommandInterpreter &interpreter) 708 : CommandObjectParsed(interpreter, "source list", 709 "Display source code for the current target " 710 "process as specified by options.", 711 nullptr, eCommandRequiresTarget) {} 712 713 ~CommandObjectSourceList() override = default; 714 715 Options *GetOptions() override { return &m_options; } 716 717 std::optional<std::string> GetRepeatCommand(Args ¤t_command_args, 718 uint32_t index) override { 719 // This is kind of gross, but the command hasn't been parsed yet so we 720 // can't look at the option values for this invocation... I have to scan 721 // the arguments directly. 722 auto iter = 723 llvm::find_if(current_command_args, [](const Args::ArgEntry &e) { 724 return e.ref() == "-r" || e.ref() == "--reverse"; 725 }); 726 if (iter == current_command_args.end()) 727 return m_cmd_name; 728 729 if (m_reverse_name.empty()) { 730 m_reverse_name = m_cmd_name; 731 m_reverse_name.append(" -r"); 732 } 733 return m_reverse_name; 734 } 735 736 protected: 737 struct SourceInfo { 738 ConstString function; 739 LineEntry line_entry; 740 741 SourceInfo(ConstString name, const LineEntry &line_entry) 742 : function(name), line_entry(line_entry) {} 743 744 SourceInfo() = default; 745 746 bool IsValid() const { return (bool)function && line_entry.IsValid(); } 747 748 bool operator==(const SourceInfo &rhs) const { 749 return function == rhs.function && 750 line_entry.original_file == rhs.line_entry.original_file && 751 line_entry.line == rhs.line_entry.line; 752 } 753 754 bool operator!=(const SourceInfo &rhs) const { 755 return function != rhs.function || 756 line_entry.original_file != rhs.line_entry.original_file || 757 line_entry.line != rhs.line_entry.line; 758 } 759 760 bool operator<(const SourceInfo &rhs) const { 761 if (function.GetCString() < rhs.function.GetCString()) 762 return true; 763 if (line_entry.file.GetDirectory().GetCString() < 764 rhs.line_entry.file.GetDirectory().GetCString()) 765 return true; 766 if (line_entry.file.GetFilename().GetCString() < 767 rhs.line_entry.file.GetFilename().GetCString()) 768 return true; 769 if (line_entry.line < rhs.line_entry.line) 770 return true; 771 return false; 772 } 773 }; 774 775 size_t DisplayFunctionSource(const SymbolContext &sc, SourceInfo &source_info, 776 CommandReturnObject &result) { 777 if (!source_info.IsValid()) { 778 source_info.function = sc.GetFunctionName(); 779 source_info.line_entry = sc.GetFunctionStartLineEntry(); 780 } 781 782 if (sc.function) { 783 Target *target = m_exe_ctx.GetTargetPtr(); 784 785 FileSpec start_file; 786 uint32_t start_line; 787 uint32_t end_line; 788 FileSpec end_file; 789 790 if (sc.block == nullptr) { 791 // Not an inlined function 792 sc.function->GetStartLineSourceInfo(start_file, start_line); 793 if (start_line == 0) { 794 result.AppendErrorWithFormat("Could not find line information for " 795 "start of function: \"%s\".\n", 796 source_info.function.GetCString()); 797 return 0; 798 } 799 sc.function->GetEndLineSourceInfo(end_file, end_line); 800 } else { 801 // We have an inlined function 802 start_file = source_info.line_entry.file; 803 start_line = source_info.line_entry.line; 804 end_line = start_line + m_options.num_lines; 805 } 806 807 // This is a little hacky, but the first line table entry for a function 808 // points to the "{" that starts the function block. It would be nice to 809 // actually get the function declaration in there too. So back up a bit, 810 // but not further than what you're going to display. 811 uint32_t extra_lines; 812 if (m_options.num_lines >= 10) 813 extra_lines = 5; 814 else 815 extra_lines = m_options.num_lines / 2; 816 uint32_t line_no; 817 if (start_line <= extra_lines) 818 line_no = 1; 819 else 820 line_no = start_line - extra_lines; 821 822 // For fun, if the function is shorter than the number of lines we're 823 // supposed to display, only display the function... 824 if (end_line != 0) { 825 if (m_options.num_lines > end_line - line_no) 826 m_options.num_lines = end_line - line_no + extra_lines; 827 } 828 829 m_breakpoint_locations.Clear(); 830 831 if (m_options.show_bp_locs) { 832 const bool show_inlines = true; 833 m_breakpoint_locations.Reset(start_file, 0, show_inlines); 834 SearchFilterForUnconstrainedSearches target_search_filter( 835 m_exe_ctx.GetTargetSP()); 836 target_search_filter.Search(m_breakpoint_locations); 837 } 838 839 result.AppendMessageWithFormat("File: %s\n", 840 start_file.GetPath().c_str()); 841 // We don't care about the column here. 842 const uint32_t column = 0; 843 return target->GetSourceManager().DisplaySourceLinesWithLineNumbers( 844 start_file, line_no, column, 0, m_options.num_lines, "", 845 &result.GetOutputStream(), GetBreakpointLocations()); 846 } else { 847 result.AppendErrorWithFormat( 848 "Could not find function info for: \"%s\".\n", 849 m_options.symbol_name.c_str()); 850 } 851 return 0; 852 } 853 854 // From Jim: The FindMatchingFunctions / FindMatchingFunctionSymbols 855 // functions "take a possibly empty vector of strings which are names of 856 // modules, and run the two search functions on the subset of the full module 857 // list that matches the strings in the input vector". If we wanted to put 858 // these somewhere, there should probably be a module-filter-list that can be 859 // passed to the various ModuleList::Find* calls, which would either be a 860 // vector of string names or a ModuleSpecList. 861 void FindMatchingFunctions(Target *target, ConstString name, 862 SymbolContextList &sc_list) { 863 // Displaying the source for a symbol: 864 if (m_options.num_lines == 0) 865 m_options.num_lines = 10; 866 867 ModuleFunctionSearchOptions function_options; 868 function_options.include_symbols = true; 869 function_options.include_inlines = false; 870 871 const size_t num_modules = m_options.modules.size(); 872 if (num_modules > 0) { 873 ModuleList matching_modules; 874 for (size_t i = 0; i < num_modules; ++i) { 875 FileSpec module_file_spec(m_options.modules[i]); 876 if (module_file_spec) { 877 ModuleSpec module_spec(module_file_spec); 878 matching_modules.Clear(); 879 target->GetImages().FindModules(module_spec, matching_modules); 880 881 matching_modules.FindFunctions(name, eFunctionNameTypeAuto, 882 function_options, sc_list); 883 } 884 } 885 } else { 886 target->GetImages().FindFunctions(name, eFunctionNameTypeAuto, 887 function_options, sc_list); 888 } 889 } 890 891 void FindMatchingFunctionSymbols(Target *target, ConstString name, 892 SymbolContextList &sc_list) { 893 const size_t num_modules = m_options.modules.size(); 894 if (num_modules > 0) { 895 ModuleList matching_modules; 896 for (size_t i = 0; i < num_modules; ++i) { 897 FileSpec module_file_spec(m_options.modules[i]); 898 if (module_file_spec) { 899 ModuleSpec module_spec(module_file_spec); 900 matching_modules.Clear(); 901 target->GetImages().FindModules(module_spec, matching_modules); 902 matching_modules.FindFunctionSymbols(name, eFunctionNameTypeAuto, 903 sc_list); 904 } 905 } 906 } else { 907 target->GetImages().FindFunctionSymbols(name, eFunctionNameTypeAuto, 908 sc_list); 909 } 910 } 911 912 bool DoExecute(Args &command, CommandReturnObject &result) override { 913 Target *target = m_exe_ctx.GetTargetPtr(); 914 915 if (!m_options.symbol_name.empty()) { 916 SymbolContextList sc_list; 917 ConstString name(m_options.symbol_name.c_str()); 918 919 // Displaying the source for a symbol. Search for function named name. 920 FindMatchingFunctions(target, name, sc_list); 921 if (sc_list.GetSize() == 0) { 922 // If we didn't find any functions with that name, try searching for 923 // symbols that line up exactly with function addresses. 924 SymbolContextList sc_list_symbols; 925 FindMatchingFunctionSymbols(target, name, sc_list_symbols); 926 for (const SymbolContext &sc : sc_list_symbols) { 927 if (sc.symbol && sc.symbol->ValueIsAddress()) { 928 const Address &base_address = sc.symbol->GetAddressRef(); 929 Function *function = base_address.CalculateSymbolContextFunction(); 930 if (function) { 931 sc_list.Append(SymbolContext(function)); 932 break; 933 } 934 } 935 } 936 } 937 938 if (sc_list.GetSize() == 0) { 939 result.AppendErrorWithFormat("Could not find function named: \"%s\".\n", 940 m_options.symbol_name.c_str()); 941 return false; 942 } 943 944 std::set<SourceInfo> source_match_set; 945 bool displayed_something = false; 946 for (const SymbolContext &sc : sc_list) { 947 SourceInfo source_info(sc.GetFunctionName(), 948 sc.GetFunctionStartLineEntry()); 949 if (source_info.IsValid() && 950 source_match_set.find(source_info) == source_match_set.end()) { 951 source_match_set.insert(source_info); 952 if (DisplayFunctionSource(sc, source_info, result)) 953 displayed_something = true; 954 } 955 } 956 if (displayed_something) 957 result.SetStatus(eReturnStatusSuccessFinishResult); 958 else 959 result.SetStatus(eReturnStatusFailed); 960 return result.Succeeded(); 961 } else if (m_options.address != LLDB_INVALID_ADDRESS) { 962 Address so_addr; 963 StreamString error_strm; 964 SymbolContextList sc_list; 965 966 if (target->GetSectionLoadList().IsEmpty()) { 967 // The target isn't loaded yet, we need to lookup the file address in 968 // all modules 969 const ModuleList &module_list = target->GetImages(); 970 const size_t num_modules = module_list.GetSize(); 971 for (size_t i = 0; i < num_modules; ++i) { 972 ModuleSP module_sp(module_list.GetModuleAtIndex(i)); 973 if (module_sp && 974 module_sp->ResolveFileAddress(m_options.address, so_addr)) { 975 SymbolContext sc; 976 sc.Clear(true); 977 if (module_sp->ResolveSymbolContextForAddress( 978 so_addr, eSymbolContextEverything, sc) & 979 eSymbolContextLineEntry) 980 sc_list.Append(sc); 981 } 982 } 983 984 if (sc_list.GetSize() == 0) { 985 result.AppendErrorWithFormat( 986 "no modules have source information for file address 0x%" PRIx64 987 ".\n", 988 m_options.address); 989 return false; 990 } 991 } else { 992 // The target has some things loaded, resolve this address to a compile 993 // unit + file + line and display 994 if (target->GetSectionLoadList().ResolveLoadAddress(m_options.address, 995 so_addr)) { 996 ModuleSP module_sp(so_addr.GetModule()); 997 if (module_sp) { 998 SymbolContext sc; 999 sc.Clear(true); 1000 if (module_sp->ResolveSymbolContextForAddress( 1001 so_addr, eSymbolContextEverything, sc) & 1002 eSymbolContextLineEntry) { 1003 sc_list.Append(sc); 1004 } else { 1005 so_addr.Dump(&error_strm, nullptr, 1006 Address::DumpStyleModuleWithFileAddress); 1007 result.AppendErrorWithFormat("address resolves to %s, but there " 1008 "is no line table information " 1009 "available for this address.\n", 1010 error_strm.GetData()); 1011 return false; 1012 } 1013 } 1014 } 1015 1016 if (sc_list.GetSize() == 0) { 1017 result.AppendErrorWithFormat( 1018 "no modules contain load address 0x%" PRIx64 ".\n", 1019 m_options.address); 1020 return false; 1021 } 1022 } 1023 for (const SymbolContext &sc : sc_list) { 1024 if (sc.comp_unit) { 1025 if (m_options.show_bp_locs) { 1026 m_breakpoint_locations.Clear(); 1027 const bool show_inlines = true; 1028 m_breakpoint_locations.Reset(sc.comp_unit->GetPrimaryFile(), 0, 1029 show_inlines); 1030 SearchFilterForUnconstrainedSearches target_search_filter( 1031 target->shared_from_this()); 1032 target_search_filter.Search(m_breakpoint_locations); 1033 } 1034 1035 bool show_fullpaths = true; 1036 bool show_module = true; 1037 bool show_inlined_frames = true; 1038 const bool show_function_arguments = true; 1039 const bool show_function_name = true; 1040 sc.DumpStopContext(&result.GetOutputStream(), 1041 m_exe_ctx.GetBestExecutionContextScope(), 1042 sc.line_entry.range.GetBaseAddress(), 1043 show_fullpaths, show_module, show_inlined_frames, 1044 show_function_arguments, show_function_name); 1045 result.GetOutputStream().EOL(); 1046 1047 if (m_options.num_lines == 0) 1048 m_options.num_lines = 10; 1049 1050 size_t lines_to_back_up = 1051 m_options.num_lines >= 10 ? 5 : m_options.num_lines / 2; 1052 1053 const uint32_t column = 1054 (GetDebugger().GetStopShowColumn() != eStopShowColumnNone) 1055 ? sc.line_entry.column 1056 : 0; 1057 target->GetSourceManager().DisplaySourceLinesWithLineNumbers( 1058 sc.comp_unit->GetPrimaryFile(), sc.line_entry.line, column, 1059 lines_to_back_up, m_options.num_lines - lines_to_back_up, "->", 1060 &result.GetOutputStream(), GetBreakpointLocations()); 1061 result.SetStatus(eReturnStatusSuccessFinishResult); 1062 } 1063 } 1064 } else if (m_options.file_name.empty()) { 1065 // Last valid source manager context, or the current frame if no valid 1066 // last context in source manager. One little trick here, if you type the 1067 // exact same list command twice in a row, it is more likely because you 1068 // typed it once, then typed it again 1069 if (m_options.start_line == 0) { 1070 if (target->GetSourceManager().DisplayMoreWithLineNumbers( 1071 &result.GetOutputStream(), m_options.num_lines, 1072 m_options.reverse, GetBreakpointLocations())) { 1073 result.SetStatus(eReturnStatusSuccessFinishResult); 1074 } 1075 } else { 1076 if (m_options.num_lines == 0) 1077 m_options.num_lines = 10; 1078 1079 if (m_options.show_bp_locs) { 1080 SourceManager::FileSP last_file_sp( 1081 target->GetSourceManager().GetLastFile()); 1082 if (last_file_sp) { 1083 const bool show_inlines = true; 1084 m_breakpoint_locations.Reset(last_file_sp->GetFileSpec(), 0, 1085 show_inlines); 1086 SearchFilterForUnconstrainedSearches target_search_filter( 1087 target->shared_from_this()); 1088 target_search_filter.Search(m_breakpoint_locations); 1089 } 1090 } else 1091 m_breakpoint_locations.Clear(); 1092 1093 const uint32_t column = 0; 1094 if (target->GetSourceManager() 1095 .DisplaySourceLinesWithLineNumbersUsingLastFile( 1096 m_options.start_line, // Line to display 1097 m_options.num_lines, // Lines after line to 1098 UINT32_MAX, // Don't mark "line" 1099 column, 1100 "", // Don't mark "line" 1101 &result.GetOutputStream(), GetBreakpointLocations())) { 1102 result.SetStatus(eReturnStatusSuccessFinishResult); 1103 } 1104 } 1105 } else { 1106 const char *filename = m_options.file_name.c_str(); 1107 1108 bool check_inlines = false; 1109 SymbolContextList sc_list; 1110 size_t num_matches = 0; 1111 1112 if (!m_options.modules.empty()) { 1113 ModuleList matching_modules; 1114 for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) { 1115 FileSpec module_file_spec(m_options.modules[i]); 1116 if (module_file_spec) { 1117 ModuleSpec module_spec(module_file_spec); 1118 matching_modules.Clear(); 1119 target->GetImages().FindModules(module_spec, matching_modules); 1120 num_matches += matching_modules.ResolveSymbolContextForFilePath( 1121 filename, 0, check_inlines, 1122 SymbolContextItem(eSymbolContextModule | 1123 eSymbolContextCompUnit), 1124 sc_list); 1125 } 1126 } 1127 } else { 1128 num_matches = target->GetImages().ResolveSymbolContextForFilePath( 1129 filename, 0, check_inlines, 1130 eSymbolContextModule | eSymbolContextCompUnit, sc_list); 1131 } 1132 1133 if (num_matches == 0) { 1134 result.AppendErrorWithFormat("Could not find source file \"%s\".\n", 1135 m_options.file_name.c_str()); 1136 return false; 1137 } 1138 1139 if (num_matches > 1) { 1140 bool got_multiple = false; 1141 CompileUnit *test_cu = nullptr; 1142 1143 for (const SymbolContext &sc : sc_list) { 1144 if (sc.comp_unit) { 1145 if (test_cu) { 1146 if (test_cu != sc.comp_unit) 1147 got_multiple = true; 1148 break; 1149 } else 1150 test_cu = sc.comp_unit; 1151 } 1152 } 1153 if (got_multiple) { 1154 result.AppendErrorWithFormat( 1155 "Multiple source files found matching: \"%s.\"\n", 1156 m_options.file_name.c_str()); 1157 return false; 1158 } 1159 } 1160 1161 SymbolContext sc; 1162 if (sc_list.GetContextAtIndex(0, sc)) { 1163 if (sc.comp_unit) { 1164 if (m_options.show_bp_locs) { 1165 const bool show_inlines = true; 1166 m_breakpoint_locations.Reset(sc.comp_unit->GetPrimaryFile(), 0, 1167 show_inlines); 1168 SearchFilterForUnconstrainedSearches target_search_filter( 1169 target->shared_from_this()); 1170 target_search_filter.Search(m_breakpoint_locations); 1171 } else 1172 m_breakpoint_locations.Clear(); 1173 1174 if (m_options.num_lines == 0) 1175 m_options.num_lines = 10; 1176 const uint32_t column = 0; 1177 target->GetSourceManager().DisplaySourceLinesWithLineNumbers( 1178 sc.comp_unit->GetPrimaryFile(), m_options.start_line, column, 0, 1179 m_options.num_lines, "", &result.GetOutputStream(), 1180 GetBreakpointLocations()); 1181 1182 result.SetStatus(eReturnStatusSuccessFinishResult); 1183 } else { 1184 result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n", 1185 m_options.file_name.c_str()); 1186 return false; 1187 } 1188 } 1189 } 1190 return result.Succeeded(); 1191 } 1192 1193 const SymbolContextList *GetBreakpointLocations() { 1194 if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0) 1195 return &m_breakpoint_locations.GetFileLineMatches(); 1196 return nullptr; 1197 } 1198 1199 CommandOptions m_options; 1200 FileLineResolver m_breakpoint_locations; 1201 std::string m_reverse_name; 1202 }; 1203 1204 #pragma mark CommandObjectMultiwordSource 1205 // CommandObjectMultiwordSource 1206 1207 CommandObjectMultiwordSource::CommandObjectMultiwordSource( 1208 CommandInterpreter &interpreter) 1209 : CommandObjectMultiword(interpreter, "source", 1210 "Commands for examining " 1211 "source code described by " 1212 "debug information for the " 1213 "current target process.", 1214 "source <subcommand> [<subcommand-options>]") { 1215 LoadSubCommand("info", 1216 CommandObjectSP(new CommandObjectSourceInfo(interpreter))); 1217 LoadSubCommand("list", 1218 CommandObjectSP(new CommandObjectSourceList(interpreter))); 1219 } 1220 1221 CommandObjectMultiwordSource::~CommandObjectMultiwordSource() = default; 1222