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