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