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