1 //===-- CommandObjectSource.cpp ---------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "CommandObjectSource.h" 11 12 // C Includes 13 // C++ Includes 14 // Other libraries and framework includes 15 // Project includes 16 #include "lldb/Core/Debugger.h" 17 #include "lldb/Core/FileLineResolver.h" 18 #include "lldb/Core/Module.h" 19 #include "lldb/Core/ModuleSpec.h" 20 #include "lldb/Core/SourceManager.h" 21 #include "lldb/Host/OptionParser.h" 22 #include "lldb/Interpreter/CommandCompletions.h" 23 #include "lldb/Interpreter/CommandInterpreter.h" 24 #include "lldb/Interpreter/CommandReturnObject.h" 25 #include "lldb/Interpreter/Options.h" 26 #include "lldb/Symbol/CompileUnit.h" 27 #include "lldb/Symbol/Function.h" 28 #include "lldb/Symbol/Symbol.h" 29 #include "lldb/Target/Process.h" 30 #include "lldb/Target/SectionLoadList.h" 31 #include "lldb/Target/StackFrame.h" 32 #include "lldb/Target/TargetList.h" 33 #include "lldb/Utility/FileSpec.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 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 63 ExecutionContext *execution_context) override { 64 Status 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 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 687 ExecutionContext *execution_context) override { 688 Status 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 values for this invocation... I have to scan the 778 // arguments directly. 779 auto iter = 780 llvm::find_if(current_command_args, [](const Args::ArgEntry &e) { 781 return e.ref == "-r" || e.ref == "--reverse"; 782 }); 783 if (iter == current_command_args.end()) 784 return m_cmd_name.c_str(); 785 786 if (m_reverse_name.empty()) { 787 m_reverse_name = m_cmd_name; 788 m_reverse_name.append(" -r"); 789 } 790 return m_reverse_name.c_str(); 791 } 792 793 protected: 794 struct SourceInfo { 795 ConstString function; 796 LineEntry line_entry; 797 798 SourceInfo(const ConstString &name, const LineEntry &line_entry) 799 : function(name), line_entry(line_entry) {} 800 801 SourceInfo() : function(), line_entry() {} 802 803 bool IsValid() const { return (bool)function && line_entry.IsValid(); } 804 805 bool operator==(const SourceInfo &rhs) const { 806 return function == rhs.function && 807 line_entry.original_file == rhs.line_entry.original_file && 808 line_entry.line == rhs.line_entry.line; 809 } 810 811 bool operator!=(const SourceInfo &rhs) const { 812 return function != rhs.function || 813 line_entry.original_file != rhs.line_entry.original_file || 814 line_entry.line != rhs.line_entry.line; 815 } 816 817 bool operator<(const SourceInfo &rhs) const { 818 if (function.GetCString() < rhs.function.GetCString()) 819 return true; 820 if (line_entry.file.GetDirectory().GetCString() < 821 rhs.line_entry.file.GetDirectory().GetCString()) 822 return true; 823 if (line_entry.file.GetFilename().GetCString() < 824 rhs.line_entry.file.GetFilename().GetCString()) 825 return true; 826 if (line_entry.line < rhs.line_entry.line) 827 return true; 828 return false; 829 } 830 }; 831 832 size_t DisplayFunctionSource(const SymbolContext &sc, SourceInfo &source_info, 833 CommandReturnObject &result) { 834 if (!source_info.IsValid()) { 835 source_info.function = sc.GetFunctionName(); 836 source_info.line_entry = sc.GetFunctionStartLineEntry(); 837 } 838 839 if (sc.function) { 840 Target *target = m_exe_ctx.GetTargetPtr(); 841 842 FileSpec start_file; 843 uint32_t start_line; 844 uint32_t end_line; 845 FileSpec end_file; 846 847 if (sc.block == nullptr) { 848 // Not an inlined function 849 sc.function->GetStartLineSourceInfo(start_file, start_line); 850 if (start_line == 0) { 851 result.AppendErrorWithFormat("Could not find line information for " 852 "start of function: \"%s\".\n", 853 source_info.function.GetCString()); 854 result.SetStatus(eReturnStatusFailed); 855 return 0; 856 } 857 sc.function->GetEndLineSourceInfo(end_file, end_line); 858 } else { 859 // We have an inlined function 860 start_file = source_info.line_entry.file; 861 start_line = source_info.line_entry.line; 862 end_line = start_line + m_options.num_lines; 863 } 864 865 // This is a little hacky, but the first line table entry for a function 866 // points to the "{" that 867 // starts the function block. It would be nice to actually get the 868 // function 869 // declaration in there too. So back up a bit, but not further than what 870 // you're going to display. 871 uint32_t extra_lines; 872 if (m_options.num_lines >= 10) 873 extra_lines = 5; 874 else 875 extra_lines = m_options.num_lines / 2; 876 uint32_t line_no; 877 if (start_line <= extra_lines) 878 line_no = 1; 879 else 880 line_no = start_line - extra_lines; 881 882 // For fun, if the function is shorter than the number of lines we're 883 // supposed to display, 884 // only display the function... 885 if (end_line != 0) { 886 if (m_options.num_lines > end_line - line_no) 887 m_options.num_lines = end_line - line_no + extra_lines; 888 } 889 890 m_breakpoint_locations.Clear(); 891 892 if (m_options.show_bp_locs) { 893 const bool show_inlines = true; 894 m_breakpoint_locations.Reset(start_file, 0, show_inlines); 895 SearchFilterForUnconstrainedSearches target_search_filter( 896 m_exe_ctx.GetTargetSP()); 897 target_search_filter.Search(m_breakpoint_locations); 898 } 899 900 result.AppendMessageWithFormat("File: %s\n", 901 start_file.GetPath().c_str()); 902 // We don't care about the column here. 903 const uint32_t column = 0; 904 return target->GetSourceManager().DisplaySourceLinesWithLineNumbers( 905 start_file, line_no, 0, m_options.num_lines, column, "", 906 &result.GetOutputStream(), GetBreakpointLocations()); 907 } else { 908 result.AppendErrorWithFormat( 909 "Could not find function info for: \"%s\".\n", 910 m_options.symbol_name.c_str()); 911 } 912 return 0; 913 } 914 915 // From Jim: The FindMatchingFunctions / FindMatchingFunctionSymbols functions 916 // "take a possibly empty vector of strings which are names of modules, and 917 // run the two search functions on the subset of the full module list that 918 // matches the strings in the input vector". If we wanted to put these 919 // somewhere, 920 // there should probably be a module-filter-list that can be passed to the 921 // various ModuleList::Find* calls, which would either be a vector of string 922 // names or a ModuleSpecList. 923 size_t FindMatchingFunctions(Target *target, const ConstString &name, 924 SymbolContextList &sc_list) { 925 // Displaying the source for a symbol: 926 bool include_inlines = true; 927 bool append = true; 928 bool include_symbols = false; 929 size_t num_matches = 0; 930 931 if (m_options.num_lines == 0) 932 m_options.num_lines = 10; 933 934 const size_t num_modules = m_options.modules.size(); 935 if (num_modules > 0) { 936 ModuleList matching_modules; 937 for (size_t i = 0; i < num_modules; ++i) { 938 FileSpec module_file_spec(m_options.modules[i], false); 939 if (module_file_spec) { 940 ModuleSpec module_spec(module_file_spec); 941 matching_modules.Clear(); 942 target->GetImages().FindModules(module_spec, matching_modules); 943 num_matches += matching_modules.FindFunctions( 944 name, eFunctionNameTypeAuto, include_symbols, include_inlines, 945 append, sc_list); 946 } 947 } 948 } else { 949 num_matches = target->GetImages().FindFunctions( 950 name, eFunctionNameTypeAuto, include_symbols, include_inlines, append, 951 sc_list); 952 } 953 return num_matches; 954 } 955 956 size_t FindMatchingFunctionSymbols(Target *target, const ConstString &name, 957 SymbolContextList &sc_list) { 958 size_t num_matches = 0; 959 const size_t num_modules = m_options.modules.size(); 960 if (num_modules > 0) { 961 ModuleList matching_modules; 962 for (size_t i = 0; i < num_modules; ++i) { 963 FileSpec module_file_spec(m_options.modules[i], false); 964 if (module_file_spec) { 965 ModuleSpec module_spec(module_file_spec); 966 matching_modules.Clear(); 967 target->GetImages().FindModules(module_spec, matching_modules); 968 num_matches += matching_modules.FindFunctionSymbols( 969 name, eFunctionNameTypeAuto, sc_list); 970 } 971 } 972 } else { 973 num_matches = target->GetImages().FindFunctionSymbols( 974 name, eFunctionNameTypeAuto, sc_list); 975 } 976 return num_matches; 977 } 978 979 bool DoExecute(Args &command, CommandReturnObject &result) override { 980 const size_t argc = command.GetArgumentCount(); 981 982 if (argc != 0) { 983 result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n", 984 GetCommandName().str().c_str()); 985 result.SetStatus(eReturnStatusFailed); 986 return false; 987 } 988 989 Target *target = m_exe_ctx.GetTargetPtr(); 990 991 if (!m_options.symbol_name.empty()) { 992 SymbolContextList sc_list; 993 ConstString name(m_options.symbol_name.c_str()); 994 995 // Displaying the source for a symbol. Search for function named name. 996 size_t num_matches = FindMatchingFunctions(target, name, sc_list); 997 if (!num_matches) { 998 // If we didn't find any functions with that name, try searching for 999 // symbols 1000 // that line up exactly with function addresses. 1001 SymbolContextList sc_list_symbols; 1002 size_t num_symbol_matches = 1003 FindMatchingFunctionSymbols(target, name, sc_list_symbols); 1004 for (size_t i = 0; i < num_symbol_matches; i++) { 1005 SymbolContext sc; 1006 sc_list_symbols.GetContextAtIndex(i, sc); 1007 if (sc.symbol && sc.symbol->ValueIsAddress()) { 1008 const Address &base_address = sc.symbol->GetAddressRef(); 1009 Function *function = base_address.CalculateSymbolContextFunction(); 1010 if (function) { 1011 sc_list.Append(SymbolContext(function)); 1012 num_matches++; 1013 break; 1014 } 1015 } 1016 } 1017 } 1018 1019 if (num_matches == 0) { 1020 result.AppendErrorWithFormat("Could not find function named: \"%s\".\n", 1021 m_options.symbol_name.c_str()); 1022 result.SetStatus(eReturnStatusFailed); 1023 return false; 1024 } 1025 1026 if (num_matches > 1) { 1027 std::set<SourceInfo> source_match_set; 1028 1029 bool displayed_something = false; 1030 for (size_t i = 0; i < num_matches; i++) { 1031 SymbolContext sc; 1032 sc_list.GetContextAtIndex(i, sc); 1033 SourceInfo source_info(sc.GetFunctionName(), 1034 sc.GetFunctionStartLineEntry()); 1035 1036 if (source_info.IsValid()) { 1037 if (source_match_set.find(source_info) == source_match_set.end()) { 1038 source_match_set.insert(source_info); 1039 if (DisplayFunctionSource(sc, source_info, result)) 1040 displayed_something = true; 1041 } 1042 } 1043 } 1044 1045 if (displayed_something) 1046 result.SetStatus(eReturnStatusSuccessFinishResult); 1047 else 1048 result.SetStatus(eReturnStatusFailed); 1049 } else { 1050 SymbolContext sc; 1051 sc_list.GetContextAtIndex(0, sc); 1052 SourceInfo source_info; 1053 1054 if (DisplayFunctionSource(sc, source_info, result)) { 1055 result.SetStatus(eReturnStatusSuccessFinishResult); 1056 } else { 1057 result.SetStatus(eReturnStatusFailed); 1058 } 1059 } 1060 return result.Succeeded(); 1061 } else if (m_options.address != LLDB_INVALID_ADDRESS) { 1062 Address so_addr; 1063 StreamString error_strm; 1064 SymbolContextList sc_list; 1065 1066 if (target->GetSectionLoadList().IsEmpty()) { 1067 // The target isn't loaded yet, we need to lookup the file address 1068 // in all modules 1069 const ModuleList &module_list = target->GetImages(); 1070 const size_t num_modules = module_list.GetSize(); 1071 for (size_t i = 0; i < num_modules; ++i) { 1072 ModuleSP module_sp(module_list.GetModuleAtIndex(i)); 1073 if (module_sp && 1074 module_sp->ResolveFileAddress(m_options.address, so_addr)) { 1075 SymbolContext sc; 1076 sc.Clear(true); 1077 if (module_sp->ResolveSymbolContextForAddress( 1078 so_addr, eSymbolContextEverything, sc) & 1079 eSymbolContextLineEntry) 1080 sc_list.Append(sc); 1081 } 1082 } 1083 1084 if (sc_list.GetSize() == 0) { 1085 result.AppendErrorWithFormat( 1086 "no modules have source information for file address 0x%" PRIx64 1087 ".\n", 1088 m_options.address); 1089 result.SetStatus(eReturnStatusFailed); 1090 return false; 1091 } 1092 } else { 1093 // The target has some things loaded, resolve this address to a 1094 // compile unit + file + line and display 1095 if (target->GetSectionLoadList().ResolveLoadAddress(m_options.address, 1096 so_addr)) { 1097 ModuleSP module_sp(so_addr.GetModule()); 1098 if (module_sp) { 1099 SymbolContext sc; 1100 sc.Clear(true); 1101 if (module_sp->ResolveSymbolContextForAddress( 1102 so_addr, eSymbolContextEverything, sc) & 1103 eSymbolContextLineEntry) { 1104 sc_list.Append(sc); 1105 } else { 1106 so_addr.Dump(&error_strm, nullptr, 1107 Address::DumpStyleModuleWithFileAddress); 1108 result.AppendErrorWithFormat("address resolves to %s, but there " 1109 "is no line table information " 1110 "available for this address.\n", 1111 error_strm.GetData()); 1112 result.SetStatus(eReturnStatusFailed); 1113 return false; 1114 } 1115 } 1116 } 1117 1118 if (sc_list.GetSize() == 0) { 1119 result.AppendErrorWithFormat( 1120 "no modules contain load address 0x%" PRIx64 ".\n", 1121 m_options.address); 1122 result.SetStatus(eReturnStatusFailed); 1123 return false; 1124 } 1125 } 1126 uint32_t num_matches = sc_list.GetSize(); 1127 for (uint32_t i = 0; i < num_matches; ++i) { 1128 SymbolContext sc; 1129 sc_list.GetContextAtIndex(i, sc); 1130 if (sc.comp_unit) { 1131 if (m_options.show_bp_locs) { 1132 m_breakpoint_locations.Clear(); 1133 const bool show_inlines = true; 1134 m_breakpoint_locations.Reset(*sc.comp_unit, 0, show_inlines); 1135 SearchFilterForUnconstrainedSearches target_search_filter( 1136 target->shared_from_this()); 1137 target_search_filter.Search(m_breakpoint_locations); 1138 } 1139 1140 bool show_fullpaths = true; 1141 bool show_module = true; 1142 bool show_inlined_frames = true; 1143 const bool show_function_arguments = true; 1144 const bool show_function_name = true; 1145 sc.DumpStopContext(&result.GetOutputStream(), 1146 m_exe_ctx.GetBestExecutionContextScope(), 1147 sc.line_entry.range.GetBaseAddress(), 1148 show_fullpaths, show_module, show_inlined_frames, 1149 show_function_arguments, show_function_name); 1150 result.GetOutputStream().EOL(); 1151 1152 if (m_options.num_lines == 0) 1153 m_options.num_lines = 10; 1154 1155 size_t lines_to_back_up = 1156 m_options.num_lines >= 10 ? 5 : m_options.num_lines / 2; 1157 1158 const uint32_t column = 1159 (m_interpreter.GetDebugger().GetStopShowColumn() != 1160 eStopShowColumnNone) 1161 ? sc.line_entry.column 1162 : 0; 1163 target->GetSourceManager().DisplaySourceLinesWithLineNumbers( 1164 sc.comp_unit, sc.line_entry.line, lines_to_back_up, column, 1165 m_options.num_lines - lines_to_back_up, "->", 1166 &result.GetOutputStream(), GetBreakpointLocations()); 1167 result.SetStatus(eReturnStatusSuccessFinishResult); 1168 } 1169 } 1170 } else if (m_options.file_name.empty()) { 1171 // Last valid source manager context, or the current frame if no 1172 // valid last context in source manager. 1173 // One little trick here, if you type the exact same list command twice in 1174 // a row, it is 1175 // more likely because you typed it once, then typed it again 1176 if (m_options.start_line == 0) { 1177 if (target->GetSourceManager().DisplayMoreWithLineNumbers( 1178 &result.GetOutputStream(), m_options.num_lines, 1179 m_options.reverse, GetBreakpointLocations())) { 1180 result.SetStatus(eReturnStatusSuccessFinishResult); 1181 } 1182 } else { 1183 if (m_options.num_lines == 0) 1184 m_options.num_lines = 10; 1185 1186 if (m_options.show_bp_locs) { 1187 SourceManager::FileSP last_file_sp( 1188 target->GetSourceManager().GetLastFile()); 1189 if (last_file_sp) { 1190 const bool show_inlines = true; 1191 m_breakpoint_locations.Reset(last_file_sp->GetFileSpec(), 0, 1192 show_inlines); 1193 SearchFilterForUnconstrainedSearches target_search_filter( 1194 target->shared_from_this()); 1195 target_search_filter.Search(m_breakpoint_locations); 1196 } 1197 } else 1198 m_breakpoint_locations.Clear(); 1199 1200 const uint32_t column = 0; 1201 if (target->GetSourceManager() 1202 .DisplaySourceLinesWithLineNumbersUsingLastFile( 1203 m_options.start_line, // Line to display 1204 m_options.num_lines, // Lines after line to 1205 UINT32_MAX, // Don't mark "line" 1206 column, 1207 "", // Don't mark "line" 1208 &result.GetOutputStream(), GetBreakpointLocations())) { 1209 result.SetStatus(eReturnStatusSuccessFinishResult); 1210 } 1211 } 1212 } else { 1213 const char *filename = m_options.file_name.c_str(); 1214 1215 bool check_inlines = false; 1216 SymbolContextList sc_list; 1217 size_t num_matches = 0; 1218 1219 if (!m_options.modules.empty()) { 1220 ModuleList matching_modules; 1221 for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) { 1222 FileSpec module_file_spec(m_options.modules[i], false); 1223 if (module_file_spec) { 1224 ModuleSpec module_spec(module_file_spec); 1225 matching_modules.Clear(); 1226 target->GetImages().FindModules(module_spec, matching_modules); 1227 num_matches += matching_modules.ResolveSymbolContextForFilePath( 1228 filename, 0, check_inlines, 1229 eSymbolContextModule | eSymbolContextCompUnit, sc_list); 1230 } 1231 } 1232 } else { 1233 num_matches = target->GetImages().ResolveSymbolContextForFilePath( 1234 filename, 0, check_inlines, 1235 eSymbolContextModule | eSymbolContextCompUnit, sc_list); 1236 } 1237 1238 if (num_matches == 0) { 1239 result.AppendErrorWithFormat("Could not find source file \"%s\".\n", 1240 m_options.file_name.c_str()); 1241 result.SetStatus(eReturnStatusFailed); 1242 return false; 1243 } 1244 1245 if (num_matches > 1) { 1246 bool got_multiple = false; 1247 FileSpec *test_cu_spec = nullptr; 1248 1249 for (unsigned i = 0; i < num_matches; i++) { 1250 SymbolContext sc; 1251 sc_list.GetContextAtIndex(i, sc); 1252 if (sc.comp_unit) { 1253 if (test_cu_spec) { 1254 if (test_cu_spec != static_cast<FileSpec *>(sc.comp_unit)) 1255 got_multiple = true; 1256 break; 1257 } else 1258 test_cu_spec = sc.comp_unit; 1259 } 1260 } 1261 if (got_multiple) { 1262 result.AppendErrorWithFormat( 1263 "Multiple source files found matching: \"%s.\"\n", 1264 m_options.file_name.c_str()); 1265 result.SetStatus(eReturnStatusFailed); 1266 return false; 1267 } 1268 } 1269 1270 SymbolContext sc; 1271 if (sc_list.GetContextAtIndex(0, sc)) { 1272 if (sc.comp_unit) { 1273 if (m_options.show_bp_locs) { 1274 const bool show_inlines = true; 1275 m_breakpoint_locations.Reset(*sc.comp_unit, 0, show_inlines); 1276 SearchFilterForUnconstrainedSearches target_search_filter( 1277 target->shared_from_this()); 1278 target_search_filter.Search(m_breakpoint_locations); 1279 } else 1280 m_breakpoint_locations.Clear(); 1281 1282 if (m_options.num_lines == 0) 1283 m_options.num_lines = 10; 1284 const uint32_t column = 0; 1285 target->GetSourceManager().DisplaySourceLinesWithLineNumbers( 1286 sc.comp_unit, m_options.start_line, 0, m_options.num_lines, 1287 column, "", &result.GetOutputStream(), GetBreakpointLocations()); 1288 1289 result.SetStatus(eReturnStatusSuccessFinishResult); 1290 } else { 1291 result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n", 1292 m_options.file_name.c_str()); 1293 result.SetStatus(eReturnStatusFailed); 1294 return false; 1295 } 1296 } 1297 } 1298 return result.Succeeded(); 1299 } 1300 1301 const SymbolContextList *GetBreakpointLocations() { 1302 if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0) 1303 return &m_breakpoint_locations.GetFileLineMatches(); 1304 return nullptr; 1305 } 1306 1307 CommandOptions m_options; 1308 FileLineResolver m_breakpoint_locations; 1309 std::string m_reverse_name; 1310 }; 1311 1312 #pragma mark CommandObjectMultiwordSource 1313 //------------------------------------------------------------------------- 1314 // CommandObjectMultiwordSource 1315 //------------------------------------------------------------------------- 1316 1317 CommandObjectMultiwordSource::CommandObjectMultiwordSource( 1318 CommandInterpreter &interpreter) 1319 : CommandObjectMultiword(interpreter, "source", "Commands for examining " 1320 "source code described by " 1321 "debug information for the " 1322 "current target process.", 1323 "source <subcommand> [<subcommand-options>]") { 1324 LoadSubCommand("info", 1325 CommandObjectSP(new CommandObjectSourceInfo(interpreter))); 1326 LoadSubCommand("list", 1327 CommandObjectSP(new CommandObjectSourceList(interpreter))); 1328 } 1329 1330 CommandObjectMultiwordSource::~CommandObjectMultiwordSource() = default; 1331