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