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/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 CommandObjectSourceInfo
35// CommandObjectSourceInfo - debug line entries dumping command
36#define LLDB_OPTIONS_source_info
37#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.SetErrorStringWithFormat("invalid line number: '%s'",
54                                         option_arg.str().c_str());
55        break;
56
57      case 'e':
58        if (option_arg.getAsInteger(0, end_line))
59          error.SetErrorStringWithFormat("invalid line number: '%s'",
60                                         option_arg.str().c_str());
61        break;
62
63      case 'c':
64        if (option_arg.getAsInteger(0, num_lines))
65          error.SetErrorStringWithFormat("invalid line count: '%s'",
66                                         option_arg.str().c_str());
67        break;
68
69      case 'f':
70        file_name = 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 entries
132  // found. If module_list is set, only dump lines contained in one of the
133  // modules. If file_spec is set, only dump lines in the file. If the
134  // 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 than
136  // end_line. If the num_lines option was specified, dont print more than
137  // 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 = m_exe_ctx.GetTargetPtr();
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.file))
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 lines
191  // 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 was
193  // specified, don't print lines greater than end_line. If the num_lines
194  // 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 = m_exe_ctx.GetTargetPtr();
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 the
215        // 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 or
224          // 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, dumping
236          // 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.file);
243            if (!cu_header_printed) {
244              if (num_matches > 0)
245                strm << "\n\n";
246              strm << "Lines found for file " << file_spec_name
247                   << " 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 the
271  // number of entries found. If module_list is set, only dump lines contained
272  // in one of the modules. If the start_line option was specified, don't print
273  // lines less than start_line. If the end_line option was specified, don't
274  // 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 ones
281      // 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 all
295  // line entries containing the address found in the modules and return the
296  // 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 = m_exe_ctx.GetTargetPtr();
305    if (target->GetSectionLoadList().IsEmpty()) {
306      // The target isn't loaded yet, we need to lookup the file address in all
307      // 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%" PRIx64
326                          " not found in any modules.\n",
327                          addr);
328    } else {
329      // The target has some things loaded, resolve this address to a compile
330      // unit + file + line and display
331      if (target->GetSectionLoadList().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%" PRIx64
357                            " resolves to %s, but it cannot"
358                            " be found in any modules.\n",
359                            addr, addr_strm.GetData());
360        }
361      } else
362        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 in
368  // 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 = m_exe_ctx.GetTargetPtr();
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't
381    // 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 for
390      // 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 to
419        // 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          else
435            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 = m_exe_ctx.GetTargetPtr();
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%" PRIx64
476                                   ".\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 = m_exe_ctx.GetTargetPtr();
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 = m_exe_ctx.GetTargetPtr();
537    if (target == nullptr) {
538      target = GetDebugger().GetSelectedTarget().get();
539      if (target == nullptr) {
540        result.AppendError("invalid target, create a debug target using the "
541                           "'target create' command.");
542        return;
543      }
544    }
545
546    uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
547    result.GetOutputStream().SetAddressByteSize(addr_byte_size);
548    result.GetErrorStream().SetAddressByteSize(addr_byte_size);
549
550    // Collect the list of modules to search.
551    m_module_list.Clear();
552    if (!m_options.modules.empty()) {
553      for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) {
554        FileSpec module_file_spec(m_options.modules[i]);
555        if (module_file_spec) {
556          ModuleSpec module_spec(module_file_spec);
557          target->GetImages().FindModules(module_spec, m_module_list);
558          if (m_module_list.IsEmpty())
559            result.AppendWarningWithFormat("No module found for '%s'.\n",
560                                           m_options.modules[i].c_str());
561        }
562      }
563      if (!m_module_list.GetSize()) {
564        result.AppendError("No modules match the input.");
565        return;
566      }
567    } else if (target->GetImages().GetSize() == 0) {
568      result.AppendError("The target has no associated executable images.");
569      return;
570    }
571
572    // Check the arguments to see what lines we should dump.
573    if (!m_options.symbol_name.empty()) {
574      // Print lines for symbol.
575      if (DumpLinesInFunctions(result))
576        result.SetStatus(eReturnStatusSuccessFinishResult);
577      else
578        result.SetStatus(eReturnStatusFailed);
579    } else if (m_options.address != LLDB_INVALID_ADDRESS) {
580      // Print lines for an address.
581      if (DumpLinesForAddress(result))
582        result.SetStatus(eReturnStatusSuccessFinishResult);
583      else
584        result.SetStatus(eReturnStatusFailed);
585    } else if (!m_options.file_name.empty()) {
586      // Dump lines for a file.
587      if (DumpLinesForFile(result))
588        result.SetStatus(eReturnStatusSuccessFinishResult);
589      else
590        result.SetStatus(eReturnStatusFailed);
591    } else {
592      // Dump the line for the current frame.
593      if (DumpLinesForFrame(result))
594        result.SetStatus(eReturnStatusSuccessFinishResult);
595      else
596        result.SetStatus(eReturnStatusFailed);
597    }
598  }
599
600  CommandOptions m_options;
601  ModuleList m_module_list;
602};
603
604#pragma mark CommandObjectSourceList
605// CommandObjectSourceList
606#define LLDB_OPTIONS_source_list
607#include "CommandOptions.inc"
608
609class CommandObjectSourceList : public CommandObjectParsed {
610  class CommandOptions : public Options {
611  public:
612    CommandOptions() = default;
613
614    ~CommandOptions() override = default;
615
616    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
617                          ExecutionContext *execution_context) override {
618      Status error;
619      const int short_option = GetDefinitions()[option_idx].short_option;
620      switch (short_option) {
621      case 'l':
622        if (option_arg.getAsInteger(0, start_line))
623          error.SetErrorStringWithFormat("invalid line number: '%s'",
624                                         option_arg.str().c_str());
625        break;
626
627      case 'c':
628        if (option_arg.getAsInteger(0, num_lines))
629          error.SetErrorStringWithFormat("invalid line count: '%s'",
630                                         option_arg.str().c_str());
631        break;
632
633      case 'f':
634        file_name = std::string(option_arg);
635        break;
636
637      case 'n':
638        symbol_name = std::string(option_arg);
639        break;
640
641      case 'a': {
642        address = OptionArgParser::ToAddress(execution_context, option_arg,
643                                             LLDB_INVALID_ADDRESS, &error);
644      } break;
645      case 's':
646        modules.push_back(std::string(option_arg));
647        break;
648
649      case 'b':
650        show_bp_locs = true;
651        break;
652      case 'r':
653        reverse = true;
654        break;
655      case 'y':
656      {
657        OptionValueFileColonLine value;
658        Status fcl_err = value.SetValueFromString(option_arg);
659        if (!fcl_err.Success()) {
660          error.SetErrorStringWithFormat(
661              "Invalid value for file:line specifier: %s",
662              fcl_err.AsCString());
663        } else {
664          file_name = value.GetFileSpec().GetPath();
665          start_line = value.GetLineNumber();
666          // I don't see anything useful to do with a column number, but I don't
667          // want to complain since someone may well have cut and pasted a
668          // listing from somewhere that included a column.
669        }
670      } break;
671      default:
672        llvm_unreachable("Unimplemented option");
673      }
674
675      return error;
676    }
677
678    void OptionParsingStarting(ExecutionContext *execution_context) override {
679      file_spec.Clear();
680      file_name.clear();
681      symbol_name.clear();
682      address = LLDB_INVALID_ADDRESS;
683      start_line = 0;
684      num_lines = 0;
685      show_bp_locs = false;
686      reverse = false;
687      modules.clear();
688    }
689
690    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
691      return llvm::ArrayRef(g_source_list_options);
692    }
693
694    // Instance variables to hold the values for command options.
695    FileSpec file_spec;
696    std::string file_name;
697    std::string symbol_name;
698    lldb::addr_t address;
699    uint32_t start_line;
700    uint32_t num_lines;
701    std::vector<std::string> modules;
702    bool show_bp_locs;
703    bool reverse;
704  };
705
706public:
707  CommandObjectSourceList(CommandInterpreter &interpreter)
708      : CommandObjectParsed(interpreter, "source list",
709                            "Display source code for the current target "
710                            "process as specified by options.",
711                            nullptr, eCommandRequiresTarget) {}
712
713  ~CommandObjectSourceList() override = default;
714
715  Options *GetOptions() override { return &m_options; }
716
717  std::optional<std::string> GetRepeatCommand(Args &current_command_args,
718                                              uint32_t index) override {
719    // This is kind of gross, but the command hasn't been parsed yet so we
720    // can't look at the option values for this invocation...  I have to scan
721    // the arguments directly.
722    auto iter =
723        llvm::find_if(current_command_args, [](const Args::ArgEntry &e) {
724          return e.ref() == "-r" || e.ref() == "--reverse";
725        });
726    if (iter == current_command_args.end())
727      return m_cmd_name;
728
729    if (m_reverse_name.empty()) {
730      m_reverse_name = m_cmd_name;
731      m_reverse_name.append(" -r");
732    }
733    return m_reverse_name;
734  }
735
736protected:
737  struct SourceInfo {
738    ConstString function;
739    LineEntry line_entry;
740
741    SourceInfo(ConstString name, const LineEntry &line_entry)
742        : function(name), line_entry(line_entry) {}
743
744    SourceInfo() = default;
745
746    bool IsValid() const { return (bool)function && line_entry.IsValid(); }
747
748    bool operator==(const SourceInfo &rhs) const {
749      return function == rhs.function &&
750             *line_entry.original_file_sp == *rhs.line_entry.original_file_sp &&
751             line_entry.line == rhs.line_entry.line;
752    }
753
754    bool operator!=(const SourceInfo &rhs) const {
755      return function != rhs.function ||
756             *line_entry.original_file_sp != *rhs.line_entry.original_file_sp ||
757             line_entry.line != rhs.line_entry.line;
758    }
759
760    bool operator<(const SourceInfo &rhs) const {
761      if (function.GetCString() < rhs.function.GetCString())
762        return true;
763      if (line_entry.file.GetDirectory().GetCString() <
764          rhs.line_entry.file.GetDirectory().GetCString())
765        return true;
766      if (line_entry.file.GetFilename().GetCString() <
767          rhs.line_entry.file.GetFilename().GetCString())
768        return true;
769      if (line_entry.line < rhs.line_entry.line)
770        return true;
771      return false;
772    }
773  };
774
775  size_t DisplayFunctionSource(const SymbolContext &sc, SourceInfo &source_info,
776                               CommandReturnObject &result) {
777    if (!source_info.IsValid()) {
778      source_info.function = sc.GetFunctionName();
779      source_info.line_entry = sc.GetFunctionStartLineEntry();
780    }
781
782    if (sc.function) {
783      Target *target = m_exe_ctx.GetTargetPtr();
784
785      FileSpec start_file;
786      uint32_t start_line;
787      uint32_t end_line;
788      FileSpec end_file;
789
790      if (sc.block == nullptr) {
791        // Not an inlined function
792        sc.function->GetStartLineSourceInfo(start_file, start_line);
793        if (start_line == 0) {
794          result.AppendErrorWithFormat("Could not find line information for "
795                                       "start of function: \"%s\".\n",
796                                       source_info.function.GetCString());
797          return 0;
798        }
799        sc.function->GetEndLineSourceInfo(end_file, end_line);
800      } else {
801        // We have an inlined function
802        start_file = source_info.line_entry.file;
803        start_line = source_info.line_entry.line;
804        end_line = start_line + m_options.num_lines;
805      }
806
807      // This is a little hacky, but the first line table entry for a function
808      // points to the "{" that starts the function block.  It would be nice to
809      // actually get the function declaration in there too.  So back up a bit,
810      // but not further than what you're going to display.
811      uint32_t extra_lines;
812      if (m_options.num_lines >= 10)
813        extra_lines = 5;
814      else
815        extra_lines = m_options.num_lines / 2;
816      uint32_t line_no;
817      if (start_line <= extra_lines)
818        line_no = 1;
819      else
820        line_no = start_line - extra_lines;
821
822      // For fun, if the function is shorter than the number of lines we're
823      // supposed to display, only display the function...
824      if (end_line != 0) {
825        if (m_options.num_lines > end_line - line_no)
826          m_options.num_lines = end_line - line_no + extra_lines;
827      }
828
829      m_breakpoint_locations.Clear();
830
831      if (m_options.show_bp_locs) {
832        const bool show_inlines = true;
833        m_breakpoint_locations.Reset(start_file, 0, show_inlines);
834        SearchFilterForUnconstrainedSearches target_search_filter(
835            m_exe_ctx.GetTargetSP());
836        target_search_filter.Search(m_breakpoint_locations);
837      }
838
839      result.AppendMessageWithFormat("File: %s\n",
840                                     start_file.GetPath().c_str());
841      // We don't care about the column here.
842      const uint32_t column = 0;
843      return target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
844          start_file, line_no, column, 0, m_options.num_lines, "",
845          &result.GetOutputStream(), GetBreakpointLocations());
846    } else {
847      result.AppendErrorWithFormat(
848          "Could not find function info for: \"%s\".\n",
849          m_options.symbol_name.c_str());
850    }
851    return 0;
852  }
853
854  // From Jim: The FindMatchingFunctions / FindMatchingFunctionSymbols
855  // functions "take a possibly empty vector of strings which are names of
856  // modules, and run the two search functions on the subset of the full module
857  // list that matches the strings in the input vector". If we wanted to put
858  // these somewhere, there should probably be a module-filter-list that can be
859  // passed to the various ModuleList::Find* calls, which would either be a
860  // vector of string names or a ModuleSpecList.
861  void FindMatchingFunctions(Target *target, ConstString name,
862                             SymbolContextList &sc_list) {
863    // Displaying the source for a symbol:
864    if (m_options.num_lines == 0)
865      m_options.num_lines = 10;
866
867    ModuleFunctionSearchOptions function_options;
868    function_options.include_symbols = true;
869    function_options.include_inlines = false;
870
871    const size_t num_modules = m_options.modules.size();
872    if (num_modules > 0) {
873      ModuleList matching_modules;
874      for (size_t i = 0; i < num_modules; ++i) {
875        FileSpec module_file_spec(m_options.modules[i]);
876        if (module_file_spec) {
877          ModuleSpec module_spec(module_file_spec);
878          matching_modules.Clear();
879          target->GetImages().FindModules(module_spec, matching_modules);
880
881          matching_modules.FindFunctions(name, eFunctionNameTypeAuto,
882                                         function_options, sc_list);
883        }
884      }
885    } else {
886      target->GetImages().FindFunctions(name, eFunctionNameTypeAuto,
887                                        function_options, sc_list);
888    }
889  }
890
891  void FindMatchingFunctionSymbols(Target *target, ConstString name,
892                                   SymbolContextList &sc_list) {
893    const size_t num_modules = m_options.modules.size();
894    if (num_modules > 0) {
895      ModuleList matching_modules;
896      for (size_t i = 0; i < num_modules; ++i) {
897        FileSpec module_file_spec(m_options.modules[i]);
898        if (module_file_spec) {
899          ModuleSpec module_spec(module_file_spec);
900          matching_modules.Clear();
901          target->GetImages().FindModules(module_spec, matching_modules);
902          matching_modules.FindFunctionSymbols(name, eFunctionNameTypeAuto,
903                                               sc_list);
904        }
905      }
906    } else {
907      target->GetImages().FindFunctionSymbols(name, eFunctionNameTypeAuto,
908                                              sc_list);
909    }
910  }
911
912  void DoExecute(Args &command, CommandReturnObject &result) override {
913    Target *target = m_exe_ctx.GetTargetPtr();
914
915    if (!m_options.symbol_name.empty()) {
916      SymbolContextList sc_list;
917      ConstString name(m_options.symbol_name.c_str());
918
919      // Displaying the source for a symbol. Search for function named name.
920      FindMatchingFunctions(target, name, sc_list);
921      if (sc_list.GetSize() == 0) {
922        // If we didn't find any functions with that name, try searching for
923        // symbols that line up exactly with function addresses.
924        SymbolContextList sc_list_symbols;
925        FindMatchingFunctionSymbols(target, name, sc_list_symbols);
926        for (const SymbolContext &sc : sc_list_symbols) {
927          if (sc.symbol && sc.symbol->ValueIsAddress()) {
928            const Address &base_address = sc.symbol->GetAddressRef();
929            Function *function = base_address.CalculateSymbolContextFunction();
930            if (function) {
931              sc_list.Append(SymbolContext(function));
932              break;
933            }
934          }
935        }
936      }
937
938      if (sc_list.GetSize() == 0) {
939        result.AppendErrorWithFormat("Could not find function named: \"%s\".\n",
940                                     m_options.symbol_name.c_str());
941        return;
942      }
943
944      std::set<SourceInfo> source_match_set;
945      bool displayed_something = false;
946      for (const SymbolContext &sc : sc_list) {
947        SourceInfo source_info(sc.GetFunctionName(),
948                               sc.GetFunctionStartLineEntry());
949        if (source_info.IsValid() &&
950            source_match_set.find(source_info) == source_match_set.end()) {
951          source_match_set.insert(source_info);
952          if (DisplayFunctionSource(sc, source_info, result))
953            displayed_something = true;
954        }
955      }
956      if (displayed_something)
957        result.SetStatus(eReturnStatusSuccessFinishResult);
958      else
959        result.SetStatus(eReturnStatusFailed);
960      return;
961    } else if (m_options.address != LLDB_INVALID_ADDRESS) {
962      Address so_addr;
963      StreamString error_strm;
964      SymbolContextList sc_list;
965
966      if (target->GetSectionLoadList().IsEmpty()) {
967        // The target isn't loaded yet, we need to lookup the file address in
968        // all modules
969        const ModuleList &module_list = target->GetImages();
970        const size_t num_modules = module_list.GetSize();
971        for (size_t i = 0; i < num_modules; ++i) {
972          ModuleSP module_sp(module_list.GetModuleAtIndex(i));
973          if (module_sp &&
974              module_sp->ResolveFileAddress(m_options.address, so_addr)) {
975            SymbolContext sc;
976            sc.Clear(true);
977            if (module_sp->ResolveSymbolContextForAddress(
978                    so_addr, eSymbolContextEverything, sc) &
979                eSymbolContextLineEntry)
980              sc_list.Append(sc);
981          }
982        }
983
984        if (sc_list.GetSize() == 0) {
985          result.AppendErrorWithFormat(
986              "no modules have source information for file address 0x%" PRIx64
987              ".\n",
988              m_options.address);
989          return;
990        }
991      } else {
992        // The target has some things loaded, resolve this address to a compile
993        // unit + file + line and display
994        if (target->GetSectionLoadList().ResolveLoadAddress(m_options.address,
995                                                            so_addr)) {
996          ModuleSP module_sp(so_addr.GetModule());
997          if (module_sp) {
998            SymbolContext sc;
999            sc.Clear(true);
1000            if (module_sp->ResolveSymbolContextForAddress(
1001                    so_addr, eSymbolContextEverything, sc) &
1002                eSymbolContextLineEntry) {
1003              sc_list.Append(sc);
1004            } else {
1005              so_addr.Dump(&error_strm, nullptr,
1006                           Address::DumpStyleModuleWithFileAddress);
1007              result.AppendErrorWithFormat("address resolves to %s, but there "
1008                                           "is no line table information "
1009                                           "available for this address.\n",
1010                                           error_strm.GetData());
1011              return;
1012            }
1013          }
1014        }
1015
1016        if (sc_list.GetSize() == 0) {
1017          result.AppendErrorWithFormat(
1018              "no modules contain load address 0x%" PRIx64 ".\n",
1019              m_options.address);
1020          return;
1021        }
1022      }
1023      for (const SymbolContext &sc : sc_list) {
1024        if (sc.comp_unit) {
1025          if (m_options.show_bp_locs) {
1026            m_breakpoint_locations.Clear();
1027            const bool show_inlines = true;
1028            m_breakpoint_locations.Reset(sc.comp_unit->GetPrimaryFile(), 0,
1029                                         show_inlines);
1030            SearchFilterForUnconstrainedSearches target_search_filter(
1031                target->shared_from_this());
1032            target_search_filter.Search(m_breakpoint_locations);
1033          }
1034
1035          bool show_fullpaths = true;
1036          bool show_module = true;
1037          bool show_inlined_frames = true;
1038          const bool show_function_arguments = true;
1039          const bool show_function_name = true;
1040          sc.DumpStopContext(&result.GetOutputStream(),
1041                             m_exe_ctx.GetBestExecutionContextScope(),
1042                             sc.line_entry.range.GetBaseAddress(),
1043                             show_fullpaths, show_module, show_inlined_frames,
1044                             show_function_arguments, show_function_name);
1045          result.GetOutputStream().EOL();
1046
1047          if (m_options.num_lines == 0)
1048            m_options.num_lines = 10;
1049
1050          size_t lines_to_back_up =
1051              m_options.num_lines >= 10 ? 5 : m_options.num_lines / 2;
1052
1053          const uint32_t column =
1054              (GetDebugger().GetStopShowColumn() != eStopShowColumnNone)
1055                  ? sc.line_entry.column
1056                  : 0;
1057          target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
1058              sc.comp_unit->GetPrimaryFile(), sc.line_entry.line, column,
1059              lines_to_back_up, m_options.num_lines - lines_to_back_up, "->",
1060              &result.GetOutputStream(), GetBreakpointLocations());
1061          result.SetStatus(eReturnStatusSuccessFinishResult);
1062        }
1063      }
1064    } else if (m_options.file_name.empty()) {
1065      // Last valid source manager context, or the current frame if no valid
1066      // last context in source manager. One little trick here, if you type the
1067      // exact same list command twice in a row, it is more likely because you
1068      // typed it once, then typed it again
1069      if (m_options.start_line == 0) {
1070        if (target->GetSourceManager().DisplayMoreWithLineNumbers(
1071                &result.GetOutputStream(), m_options.num_lines,
1072                m_options.reverse, GetBreakpointLocations())) {
1073          result.SetStatus(eReturnStatusSuccessFinishResult);
1074        }
1075      } else {
1076        if (m_options.num_lines == 0)
1077          m_options.num_lines = 10;
1078
1079        if (m_options.show_bp_locs) {
1080          SourceManager::FileSP last_file_sp(
1081              target->GetSourceManager().GetLastFile());
1082          if (last_file_sp) {
1083            const bool show_inlines = true;
1084            m_breakpoint_locations.Reset(last_file_sp->GetFileSpec(), 0,
1085                                         show_inlines);
1086            SearchFilterForUnconstrainedSearches target_search_filter(
1087                target->shared_from_this());
1088            target_search_filter.Search(m_breakpoint_locations);
1089          }
1090        } else
1091          m_breakpoint_locations.Clear();
1092
1093        const uint32_t column = 0;
1094        if (target->GetSourceManager()
1095                .DisplaySourceLinesWithLineNumbersUsingLastFile(
1096                    m_options.start_line, // Line to display
1097                    m_options.num_lines,  // Lines after line to
1098                    UINT32_MAX,           // Don't mark "line"
1099                    column,
1100                    "", // Don't mark "line"
1101                    &result.GetOutputStream(), GetBreakpointLocations())) {
1102          result.SetStatus(eReturnStatusSuccessFinishResult);
1103        }
1104      }
1105    } else {
1106      const char *filename = m_options.file_name.c_str();
1107
1108      bool check_inlines = false;
1109      SymbolContextList sc_list;
1110      size_t num_matches = 0;
1111
1112      if (!m_options.modules.empty()) {
1113        ModuleList matching_modules;
1114        for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) {
1115          FileSpec module_file_spec(m_options.modules[i]);
1116          if (module_file_spec) {
1117            ModuleSpec module_spec(module_file_spec);
1118            matching_modules.Clear();
1119            target->GetImages().FindModules(module_spec, matching_modules);
1120            num_matches += matching_modules.ResolveSymbolContextForFilePath(
1121                filename, 0, check_inlines,
1122                SymbolContextItem(eSymbolContextModule |
1123                                  eSymbolContextCompUnit),
1124                sc_list);
1125          }
1126        }
1127      } else {
1128        num_matches = target->GetImages().ResolveSymbolContextForFilePath(
1129            filename, 0, check_inlines,
1130            eSymbolContextModule | eSymbolContextCompUnit, sc_list);
1131      }
1132
1133      if (num_matches == 0) {
1134        result.AppendErrorWithFormat("Could not find source file \"%s\".\n",
1135                                     m_options.file_name.c_str());
1136        return;
1137      }
1138
1139      if (num_matches > 1) {
1140        bool got_multiple = false;
1141        CompileUnit *test_cu = nullptr;
1142
1143        for (const SymbolContext &sc : sc_list) {
1144          if (sc.comp_unit) {
1145            if (test_cu) {
1146              if (test_cu != sc.comp_unit)
1147                got_multiple = true;
1148              break;
1149            } else
1150              test_cu = sc.comp_unit;
1151          }
1152        }
1153        if (got_multiple) {
1154          result.AppendErrorWithFormat(
1155              "Multiple source files found matching: \"%s.\"\n",
1156              m_options.file_name.c_str());
1157          return;
1158        }
1159      }
1160
1161      SymbolContext sc;
1162      if (sc_list.GetContextAtIndex(0, sc)) {
1163        if (sc.comp_unit) {
1164          if (m_options.show_bp_locs) {
1165            const bool show_inlines = true;
1166            m_breakpoint_locations.Reset(sc.comp_unit->GetPrimaryFile(), 0,
1167                                         show_inlines);
1168            SearchFilterForUnconstrainedSearches target_search_filter(
1169                target->shared_from_this());
1170            target_search_filter.Search(m_breakpoint_locations);
1171          } else
1172            m_breakpoint_locations.Clear();
1173
1174          if (m_options.num_lines == 0)
1175            m_options.num_lines = 10;
1176          const uint32_t column = 0;
1177          target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
1178              sc.comp_unit->GetPrimaryFile(), m_options.start_line, column, 0,
1179              m_options.num_lines, "", &result.GetOutputStream(),
1180              GetBreakpointLocations());
1181
1182          result.SetStatus(eReturnStatusSuccessFinishResult);
1183        } else {
1184          result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n",
1185                                       m_options.file_name.c_str());
1186        }
1187      }
1188    }
1189  }
1190
1191  const SymbolContextList *GetBreakpointLocations() {
1192    if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0)
1193      return &m_breakpoint_locations.GetFileLineMatches();
1194    return nullptr;
1195  }
1196
1197  CommandOptions m_options;
1198  FileLineResolver m_breakpoint_locations;
1199  std::string m_reverse_name;
1200};
1201
1202class CommandObjectSourceCacheDump : public CommandObjectParsed {
1203public:
1204  CommandObjectSourceCacheDump(CommandInterpreter &interpreter)
1205      : CommandObjectParsed(interpreter, "source cache dump",
1206                            "Dump the state of the source code cache. Intended "
1207                            "to be used for debugging LLDB itself.",
1208                            nullptr) {}
1209
1210  ~CommandObjectSourceCacheDump() override = default;
1211
1212protected:
1213  void DoExecute(Args &command, CommandReturnObject &result) override {
1214    // Dump the debugger source cache.
1215    result.GetOutputStream() << "Debugger Source File Cache\n";
1216    SourceManager::SourceFileCache &cache = GetDebugger().GetSourceFileCache();
1217    cache.Dump(result.GetOutputStream());
1218
1219    // Dump the process source cache.
1220    if (ProcessSP process_sp = m_exe_ctx.GetProcessSP()) {
1221      result.GetOutputStream() << "\nProcess Source File Cache\n";
1222      SourceManager::SourceFileCache &cache = process_sp->GetSourceFileCache();
1223      cache.Dump(result.GetOutputStream());
1224    }
1225
1226    result.SetStatus(eReturnStatusSuccessFinishResult);
1227  }
1228};
1229
1230class CommandObjectSourceCacheClear : public CommandObjectParsed {
1231public:
1232  CommandObjectSourceCacheClear(CommandInterpreter &interpreter)
1233      : CommandObjectParsed(interpreter, "source cache clear",
1234                            "Clear the source code cache.\n", nullptr) {}
1235
1236  ~CommandObjectSourceCacheClear() override = default;
1237
1238protected:
1239  void DoExecute(Args &command, CommandReturnObject &result) override {
1240    // Clear the debugger cache.
1241    SourceManager::SourceFileCache &cache = GetDebugger().GetSourceFileCache();
1242    cache.Clear();
1243
1244    // Clear the process cache.
1245    if (ProcessSP process_sp = m_exe_ctx.GetProcessSP())
1246      process_sp->GetSourceFileCache().Clear();
1247
1248    result.SetStatus(eReturnStatusSuccessFinishNoResult);
1249  }
1250};
1251
1252class CommandObjectSourceCache : public CommandObjectMultiword {
1253public:
1254  CommandObjectSourceCache(CommandInterpreter &interpreter)
1255      : CommandObjectMultiword(interpreter, "source cache",
1256                               "Commands for managing the source code cache.",
1257                               "source cache <sub-command>") {
1258    LoadSubCommand(
1259        "dump", CommandObjectSP(new CommandObjectSourceCacheDump(interpreter)));
1260    LoadSubCommand("clear", CommandObjectSP(new CommandObjectSourceCacheClear(
1261                                interpreter)));
1262  }
1263
1264  ~CommandObjectSourceCache() override = default;
1265
1266private:
1267  CommandObjectSourceCache(const CommandObjectSourceCache &) = delete;
1268  const CommandObjectSourceCache &
1269  operator=(const CommandObjectSourceCache &) = delete;
1270};
1271
1272#pragma mark CommandObjectMultiwordSource
1273// CommandObjectMultiwordSource
1274
1275CommandObjectMultiwordSource::CommandObjectMultiwordSource(
1276    CommandInterpreter &interpreter)
1277    : CommandObjectMultiword(interpreter, "source",
1278                             "Commands for examining "
1279                             "source code described by "
1280                             "debug information for the "
1281                             "current target process.",
1282                             "source <subcommand> [<subcommand-options>]") {
1283  LoadSubCommand("info",
1284                 CommandObjectSP(new CommandObjectSourceInfo(interpreter)));
1285  LoadSubCommand("list",
1286                 CommandObjectSP(new CommandObjectSourceList(interpreter)));
1287  LoadSubCommand("cache",
1288                 CommandObjectSP(new CommandObjectSourceCache(interpreter)));
1289}
1290
1291CommandObjectMultiwordSource::~CommandObjectMultiwordSource() = default;
1292