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