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