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