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