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