CommandObjectSource.cpp revision 360784
1//===-- CommandObjectSource.cpp ---------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "CommandObjectSource.h"
10
11#include "lldb/Core/Debugger.h"
12#include "lldb/Core/FileLineResolver.h"
13#include "lldb/Core/Module.h"
14#include "lldb/Core/ModuleSpec.h"
15#include "lldb/Core/SourceManager.h"
16#include "lldb/Host/OptionParser.h"
17#include "lldb/Interpreter/CommandReturnObject.h"
18#include "lldb/Interpreter/OptionArgParser.h"
19#include "lldb/Interpreter/Options.h"
20#include "lldb/Symbol/CompileUnit.h"
21#include "lldb/Symbol/Function.h"
22#include "lldb/Symbol/Symbol.h"
23#include "lldb/Target/SectionLoadList.h"
24#include "lldb/Target/StackFrame.h"
25#include "lldb/Utility/FileSpec.h"
26
27using namespace lldb;
28using namespace lldb_private;
29
30#pragma mark CommandObjectSourceInfo
31// CommandObjectSourceInfo - debug line entries dumping command
32#define LLDB_OPTIONS_source_info
33#include "CommandOptions.inc"
34
35class CommandObjectSourceInfo : public CommandObjectParsed {
36  class CommandOptions : public Options {
37  public:
38    CommandOptions() : Options() {}
39
40    ~CommandOptions() override = default;
41
42    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
43                          ExecutionContext *execution_context) override {
44      Status error;
45      const int short_option = GetDefinitions()[option_idx].short_option;
46      switch (short_option) {
47      case 'l':
48        if (option_arg.getAsInteger(0, start_line))
49          error.SetErrorStringWithFormat("invalid line number: '%s'",
50                                         option_arg.str().c_str());
51        break;
52
53      case 'e':
54        if (option_arg.getAsInteger(0, end_line))
55          error.SetErrorStringWithFormat("invalid line number: '%s'",
56                                         option_arg.str().c_str());
57        break;
58
59      case 'c':
60        if (option_arg.getAsInteger(0, num_lines))
61          error.SetErrorStringWithFormat("invalid line count: '%s'",
62                                         option_arg.str().c_str());
63        break;
64
65      case 'f':
66        file_name = option_arg;
67        break;
68
69      case 'n':
70        symbol_name = option_arg;
71        break;
72
73      case 'a': {
74        address = OptionArgParser::ToAddress(execution_context, option_arg,
75                                             LLDB_INVALID_ADDRESS, &error);
76      } break;
77      case 's':
78        modules.push_back(std::string(option_arg));
79        break;
80      default:
81        llvm_unreachable("Unimplemented option");
82      }
83
84      return error;
85    }
86
87    void OptionParsingStarting(ExecutionContext *execution_context) override {
88      file_spec.Clear();
89      file_name.clear();
90      symbol_name.clear();
91      address = LLDB_INVALID_ADDRESS;
92      start_line = 0;
93      end_line = 0;
94      num_lines = 0;
95      modules.clear();
96    }
97
98    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
99      return llvm::makeArrayRef(g_source_info_options);
100    }
101
102    // Instance variables to hold the values for command options.
103    FileSpec file_spec;
104    std::string file_name;
105    std::string symbol_name;
106    lldb::addr_t address;
107    uint32_t start_line;
108    uint32_t end_line;
109    uint32_t num_lines;
110    std::vector<std::string> modules;
111  };
112
113public:
114  CommandObjectSourceInfo(CommandInterpreter &interpreter)
115      : CommandObjectParsed(
116            interpreter, "source info",
117            "Display source line information for the current target "
118            "process.  Defaults to instruction pointer in current stack "
119            "frame.",
120            nullptr, eCommandRequiresTarget),
121        m_options() {}
122
123  ~CommandObjectSourceInfo() override = default;
124
125  Options *GetOptions() override { return &m_options; }
126
127protected:
128  // Dump the line entries in each symbol context. Return the number of entries
129  // found. If module_list is set, only dump lines contained in one of the
130  // modules. If file_spec is set, only dump lines in the file. If the
131  // start_line option was specified, don't print lines less than start_line.
132  // If the end_line option was specified, don't print lines greater than
133  // end_line. If the num_lines option was specified, dont print more than
134  // num_lines entries.
135  uint32_t DumpLinesInSymbolContexts(Stream &strm,
136                                     const SymbolContextList &sc_list,
137                                     const ModuleList &module_list,
138                                     const FileSpec &file_spec) {
139    uint32_t start_line = m_options.start_line;
140    uint32_t end_line = m_options.end_line;
141    uint32_t num_lines = m_options.num_lines;
142    Target *target = m_exe_ctx.GetTargetPtr();
143
144    uint32_t num_matches = 0;
145    // Dump all the line entries for the file in the list.
146    ConstString last_module_file_name;
147    uint32_t num_scs = sc_list.GetSize();
148    for (uint32_t i = 0; i < num_scs; ++i) {
149      SymbolContext sc;
150      sc_list.GetContextAtIndex(i, sc);
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 FileSpecList &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    // Note: module_list can't be const& because FindFunctionSymbols isn't
377    // const.
378    ModuleList module_list =
379        (m_module_list.GetSize() > 0) ? m_module_list : target->GetImages();
380    module_list.FindFunctions(name, eFunctionNameTypeAuto,
381                              /*include_symbols=*/false,
382                              /*include_inlines=*/true, sc_list_funcs);
383    size_t num_matches = sc_list_funcs.GetSize();
384
385    if (!num_matches) {
386      // If we didn't find any functions with that name, try searching for
387      // symbols that line up exactly with function addresses.
388      SymbolContextList sc_list_symbols;
389      module_list.FindFunctionSymbols(name, eFunctionNameTypeAuto,
390                                      sc_list_symbols);
391      size_t num_symbol_matches = sc_list_symbols.GetSize();
392      for (size_t i = 0; i < num_symbol_matches; i++) {
393        SymbolContext sc;
394        sc_list_symbols.GetContextAtIndex(i, sc);
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 (size_t i = 0; i < num_matches; i++) {
411      SymbolContext sc;
412      sc_list_funcs.GetContextAtIndex(i, sc);
413      bool context_found_for_symbol = false;
414      // Loop through all the ranges in the function.
415      AddressRange range;
416      for (uint32_t r = 0;
417           sc.GetAddressRange(eSymbolContextEverything, r,
418                              /*use_inline_block_range=*/true, range);
419           ++r) {
420        // Append the symbol contexts for each address in the range to
421        // sc_list_lines.
422        const Address &base_address = range.GetBaseAddress();
423        const addr_t size = range.GetByteSize();
424        lldb::addr_t start_addr = base_address.GetLoadAddress(target);
425        if (start_addr == LLDB_INVALID_ADDRESS)
426          start_addr = base_address.GetFileAddress();
427        lldb::addr_t end_addr = start_addr + size;
428        for (lldb::addr_t addr = start_addr; addr < end_addr;
429             addr += addr_byte_size) {
430          StreamString error_strm;
431          if (!GetSymbolContextsForAddress(module_list, addr, sc_list_lines,
432                                           error_strm))
433            result.AppendWarningWithFormat("in symbol '%s': %s",
434                                           sc.GetFunctionName().AsCString(),
435                                           error_strm.GetData());
436          else
437            context_found_for_symbol = true;
438        }
439      }
440      if (!context_found_for_symbol)
441        result.AppendWarningWithFormat("Unable to find line information"
442                                       " for matching symbol '%s'.\n",
443                                       sc.GetFunctionName().AsCString());
444    }
445    if (sc_list_lines.GetSize() == 0) {
446      result.AppendErrorWithFormat("No line information could be found"
447                                   " for any symbols matching '%s'.\n",
448                                   name.AsCString());
449      return false;
450    }
451    FileSpec file_spec;
452    if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list_lines,
453                                   module_list, file_spec)) {
454      result.AppendErrorWithFormat(
455          "Unable to dump line information for symbol '%s'.\n",
456          name.AsCString());
457      return false;
458    }
459    return true;
460  }
461
462  // Dump the line entries found for the address specified in the option.
463  bool DumpLinesForAddress(CommandReturnObject &result) {
464    Target *target = m_exe_ctx.GetTargetPtr();
465    SymbolContextList sc_list;
466
467    StreamString error_strm;
468    if (!GetSymbolContextsForAddress(target->GetImages(), m_options.address,
469                                     sc_list, error_strm)) {
470      result.AppendErrorWithFormat("%s.\n", error_strm.GetData());
471      return false;
472    }
473    ModuleList module_list;
474    FileSpec file_spec;
475    if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list,
476                                   module_list, file_spec)) {
477      result.AppendErrorWithFormat("No modules contain load address 0x%" PRIx64
478                                   ".\n",
479                                   m_options.address);
480      return false;
481    }
482    return true;
483  }
484
485  // Dump the line entries found in the file specified in the option.
486  bool DumpLinesForFile(CommandReturnObject &result) {
487    FileSpec file_spec(m_options.file_name);
488    const char *filename = m_options.file_name.c_str();
489    Target *target = m_exe_ctx.GetTargetPtr();
490    const ModuleList &module_list =
491        (m_module_list.GetSize() > 0) ? m_module_list : target->GetImages();
492
493    bool displayed_something = false;
494    const size_t num_modules = module_list.GetSize();
495    for (uint32_t i = 0; i < num_modules; ++i) {
496      // Dump lines for this module.
497      Module *module = module_list.GetModulePointerAtIndex(i);
498      assert(module);
499      if (DumpFileLinesInModule(result.GetOutputStream(), module, file_spec))
500        displayed_something = true;
501    }
502    if (!displayed_something) {
503      result.AppendErrorWithFormat("No source filenames matched '%s'.\n",
504                                   filename);
505      return false;
506    }
507    return true;
508  }
509
510  // Dump the line entries for the current frame.
511  bool DumpLinesForFrame(CommandReturnObject &result) {
512    StackFrame *cur_frame = m_exe_ctx.GetFramePtr();
513    if (cur_frame == nullptr) {
514      result.AppendError(
515          "No selected frame to use to find the default source.");
516      return false;
517    } else if (!cur_frame->HasDebugInformation()) {
518      result.AppendError("No debug info for the selected frame.");
519      return false;
520    } else {
521      const SymbolContext &sc =
522          cur_frame->GetSymbolContext(eSymbolContextLineEntry);
523      SymbolContextList sc_list;
524      sc_list.Append(sc);
525      ModuleList module_list;
526      FileSpec file_spec;
527      if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list,
528                                     module_list, file_spec)) {
529        result.AppendError(
530            "No source line info available for the selected frame.");
531        return false;
532      }
533    }
534    return true;
535  }
536
537  bool DoExecute(Args &command, CommandReturnObject &result) override {
538    const size_t argc = command.GetArgumentCount();
539
540    if (argc != 0) {
541      result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n",
542                                   GetCommandName().str().c_str());
543      result.SetStatus(eReturnStatusFailed);
544      return false;
545    }
546
547    Target *target = m_exe_ctx.GetTargetPtr();
548    if (target == nullptr) {
549      target = GetDebugger().GetSelectedTarget().get();
550      if (target == nullptr) {
551        result.AppendError("invalid target, create a debug target using the "
552                           "'target create' command.");
553        result.SetStatus(eReturnStatusFailed);
554        return false;
555      }
556    }
557
558    uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
559    result.GetOutputStream().SetAddressByteSize(addr_byte_size);
560    result.GetErrorStream().SetAddressByteSize(addr_byte_size);
561
562    // Collect the list of modules to search.
563    m_module_list.Clear();
564    if (!m_options.modules.empty()) {
565      for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) {
566        FileSpec module_file_spec(m_options.modules[i]);
567        if (module_file_spec) {
568          ModuleSpec module_spec(module_file_spec);
569          target->GetImages().FindModules(module_spec, m_module_list);
570          if (m_module_list.IsEmpty())
571            result.AppendWarningWithFormat("No module found for '%s'.\n",
572                                           m_options.modules[i].c_str());
573        }
574      }
575      if (!m_module_list.GetSize()) {
576        result.AppendError("No modules match the input.");
577        result.SetStatus(eReturnStatusFailed);
578        return false;
579      }
580    } else if (target->GetImages().GetSize() == 0) {
581      result.AppendError("The target has no associated executable images.");
582      result.SetStatus(eReturnStatusFailed);
583      return false;
584    }
585
586    // Check the arguments to see what lines we should dump.
587    if (!m_options.symbol_name.empty()) {
588      // Print lines for symbol.
589      if (DumpLinesInFunctions(result))
590        result.SetStatus(eReturnStatusSuccessFinishResult);
591      else
592        result.SetStatus(eReturnStatusFailed);
593    } else if (m_options.address != LLDB_INVALID_ADDRESS) {
594      // Print lines for an address.
595      if (DumpLinesForAddress(result))
596        result.SetStatus(eReturnStatusSuccessFinishResult);
597      else
598        result.SetStatus(eReturnStatusFailed);
599    } else if (!m_options.file_name.empty()) {
600      // Dump lines for a file.
601      if (DumpLinesForFile(result))
602        result.SetStatus(eReturnStatusSuccessFinishResult);
603      else
604        result.SetStatus(eReturnStatusFailed);
605    } else {
606      // Dump the line for the current frame.
607      if (DumpLinesForFrame(result))
608        result.SetStatus(eReturnStatusSuccessFinishResult);
609      else
610        result.SetStatus(eReturnStatusFailed);
611    }
612    return result.Succeeded();
613  }
614
615  CommandOptions m_options;
616  ModuleList m_module_list;
617};
618
619#pragma mark CommandObjectSourceList
620// CommandObjectSourceList
621#define LLDB_OPTIONS_source_list
622#include "CommandOptions.inc"
623
624class CommandObjectSourceList : public CommandObjectParsed {
625  class CommandOptions : public Options {
626  public:
627    CommandOptions() : Options() {}
628
629    ~CommandOptions() override = default;
630
631    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
632                          ExecutionContext *execution_context) override {
633      Status error;
634      const int short_option = GetDefinitions()[option_idx].short_option;
635      switch (short_option) {
636      case 'l':
637        if (option_arg.getAsInteger(0, start_line))
638          error.SetErrorStringWithFormat("invalid line number: '%s'",
639                                         option_arg.str().c_str());
640        break;
641
642      case 'c':
643        if (option_arg.getAsInteger(0, num_lines))
644          error.SetErrorStringWithFormat("invalid line count: '%s'",
645                                         option_arg.str().c_str());
646        break;
647
648      case 'f':
649        file_name = option_arg;
650        break;
651
652      case 'n':
653        symbol_name = option_arg;
654        break;
655
656      case 'a': {
657        address = OptionArgParser::ToAddress(execution_context, option_arg,
658                                             LLDB_INVALID_ADDRESS, &error);
659      } break;
660      case 's':
661        modules.push_back(std::string(option_arg));
662        break;
663
664      case 'b':
665        show_bp_locs = true;
666        break;
667      case 'r':
668        reverse = true;
669        break;
670      default:
671        llvm_unreachable("Unimplemented option");
672      }
673
674      return error;
675    }
676
677    void OptionParsingStarting(ExecutionContext *execution_context) override {
678      file_spec.Clear();
679      file_name.clear();
680      symbol_name.clear();
681      address = LLDB_INVALID_ADDRESS;
682      start_line = 0;
683      num_lines = 0;
684      show_bp_locs = false;
685      reverse = false;
686      modules.clear();
687    }
688
689    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
690      return llvm::makeArrayRef(g_source_list_options);
691    }
692
693    // Instance variables to hold the values for command options.
694    FileSpec file_spec;
695    std::string file_name;
696    std::string symbol_name;
697    lldb::addr_t address;
698    uint32_t start_line;
699    uint32_t num_lines;
700    std::vector<std::string> modules;
701    bool show_bp_locs;
702    bool reverse;
703  };
704
705public:
706  CommandObjectSourceList(CommandInterpreter &interpreter)
707      : CommandObjectParsed(interpreter, "source list",
708                            "Display source code for the current target "
709                            "process as specified by options.",
710                            nullptr, eCommandRequiresTarget),
711        m_options() {}
712
713  ~CommandObjectSourceList() override = default;
714
715  Options *GetOptions() override { return &m_options; }
716
717  const char *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.c_str();
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.c_str();
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() : function(), line_entry() {}
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 == rhs.line_entry.original_file &&
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 != rhs.line_entry.original_file ||
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          result.SetStatus(eReturnStatusFailed);
798          return 0;
799        }
800        sc.function->GetEndLineSourceInfo(end_file, end_line);
801      } else {
802        // We have an inlined function
803        start_file = source_info.line_entry.file;
804        start_line = source_info.line_entry.line;
805        end_line = start_line + m_options.num_lines;
806      }
807
808      // This is a little hacky, but the first line table entry for a function
809      // points to the "{" that starts the function block.  It would be nice to
810      // actually get the function declaration in there too.  So back up a bit,
811      // but not further than what you're going to display.
812      uint32_t extra_lines;
813      if (m_options.num_lines >= 10)
814        extra_lines = 5;
815      else
816        extra_lines = m_options.num_lines / 2;
817      uint32_t line_no;
818      if (start_line <= extra_lines)
819        line_no = 1;
820      else
821        line_no = start_line - extra_lines;
822
823      // For fun, if the function is shorter than the number of lines we're
824      // supposed to display, only display the function...
825      if (end_line != 0) {
826        if (m_options.num_lines > end_line - line_no)
827          m_options.num_lines = end_line - line_no + extra_lines;
828      }
829
830      m_breakpoint_locations.Clear();
831
832      if (m_options.show_bp_locs) {
833        const bool show_inlines = true;
834        m_breakpoint_locations.Reset(start_file, 0, show_inlines);
835        SearchFilterForUnconstrainedSearches target_search_filter(
836            m_exe_ctx.GetTargetSP());
837        target_search_filter.Search(m_breakpoint_locations);
838      }
839
840      result.AppendMessageWithFormat("File: %s\n",
841                                     start_file.GetPath().c_str());
842      // We don't care about the column here.
843      const uint32_t column = 0;
844      return target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
845          start_file, line_no, column, 0, m_options.num_lines, "",
846          &result.GetOutputStream(), GetBreakpointLocations());
847    } else {
848      result.AppendErrorWithFormat(
849          "Could not find function info for: \"%s\".\n",
850          m_options.symbol_name.c_str());
851    }
852    return 0;
853  }
854
855  // From Jim: The FindMatchingFunctions / FindMatchingFunctionSymbols
856  // functions "take a possibly empty vector of strings which are names of
857  // modules, and run the two search functions on the subset of the full module
858  // list that matches the strings in the input vector". If we wanted to put
859  // these somewhere, there should probably be a module-filter-list that can be
860  // passed to the various ModuleList::Find* calls, which would either be a
861  // vector of string names or a ModuleSpecList.
862  void FindMatchingFunctions(Target *target, ConstString name,
863                             SymbolContextList &sc_list) {
864    // Displaying the source for a symbol:
865    bool include_inlines = true;
866    bool include_symbols = false;
867
868    if (m_options.num_lines == 0)
869      m_options.num_lines = 10;
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          matching_modules.FindFunctions(name, eFunctionNameTypeAuto,
881                                         include_symbols, include_inlines,
882                                         sc_list);
883        }
884      }
885    } else {
886      target->GetImages().FindFunctions(name, eFunctionNameTypeAuto,
887                                        include_symbols, include_inlines,
888                                        sc_list);
889    }
890  }
891
892  void FindMatchingFunctionSymbols(Target *target, ConstString name,
893                                   SymbolContextList &sc_list) {
894    const size_t num_modules = m_options.modules.size();
895    if (num_modules > 0) {
896      ModuleList matching_modules;
897      for (size_t i = 0; i < num_modules; ++i) {
898        FileSpec module_file_spec(m_options.modules[i]);
899        if (module_file_spec) {
900          ModuleSpec module_spec(module_file_spec);
901          matching_modules.Clear();
902          target->GetImages().FindModules(module_spec, matching_modules);
903          matching_modules.FindFunctionSymbols(name, eFunctionNameTypeAuto,
904                                               sc_list);
905        }
906      }
907    } else {
908      target->GetImages().FindFunctionSymbols(name, eFunctionNameTypeAuto,
909                                              sc_list);
910    }
911  }
912
913  bool DoExecute(Args &command, CommandReturnObject &result) override {
914    const size_t argc = command.GetArgumentCount();
915
916    if (argc != 0) {
917      result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n",
918                                   GetCommandName().str().c_str());
919      result.SetStatus(eReturnStatusFailed);
920      return false;
921    }
922
923    Target *target = m_exe_ctx.GetTargetPtr();
924
925    if (!m_options.symbol_name.empty()) {
926      SymbolContextList sc_list;
927      ConstString name(m_options.symbol_name.c_str());
928
929      // Displaying the source for a symbol. Search for function named name.
930      FindMatchingFunctions(target, name, sc_list);
931      size_t num_matches = sc_list.GetSize();
932      if (!num_matches) {
933        // If we didn't find any functions with that name, try searching for
934        // symbols that line up exactly with function addresses.
935        SymbolContextList sc_list_symbols;
936        FindMatchingFunctionSymbols(target, name, sc_list_symbols);
937        size_t num_symbol_matches = sc_list_symbols.GetSize();
938
939        for (size_t i = 0; i < num_symbol_matches; i++) {
940          SymbolContext sc;
941          sc_list_symbols.GetContextAtIndex(i, sc);
942          if (sc.symbol && sc.symbol->ValueIsAddress()) {
943            const Address &base_address = sc.symbol->GetAddressRef();
944            Function *function = base_address.CalculateSymbolContextFunction();
945            if (function) {
946              sc_list.Append(SymbolContext(function));
947              num_matches++;
948              break;
949            }
950          }
951        }
952      }
953
954      if (num_matches == 0) {
955        result.AppendErrorWithFormat("Could not find function named: \"%s\".\n",
956                                     m_options.symbol_name.c_str());
957        result.SetStatus(eReturnStatusFailed);
958        return false;
959      }
960
961      if (num_matches > 1) {
962        std::set<SourceInfo> source_match_set;
963
964        bool displayed_something = false;
965        for (size_t i = 0; i < num_matches; i++) {
966          SymbolContext sc;
967          sc_list.GetContextAtIndex(i, sc);
968          SourceInfo source_info(sc.GetFunctionName(),
969                                 sc.GetFunctionStartLineEntry());
970
971          if (source_info.IsValid()) {
972            if (source_match_set.find(source_info) == source_match_set.end()) {
973              source_match_set.insert(source_info);
974              if (DisplayFunctionSource(sc, source_info, result))
975                displayed_something = true;
976            }
977          }
978        }
979
980        if (displayed_something)
981          result.SetStatus(eReturnStatusSuccessFinishResult);
982        else
983          result.SetStatus(eReturnStatusFailed);
984      } else {
985        SymbolContext sc;
986        sc_list.GetContextAtIndex(0, sc);
987        SourceInfo source_info;
988
989        if (DisplayFunctionSource(sc, source_info, result)) {
990          result.SetStatus(eReturnStatusSuccessFinishResult);
991        } else {
992          result.SetStatus(eReturnStatusFailed);
993        }
994      }
995      return result.Succeeded();
996    } else if (m_options.address != LLDB_INVALID_ADDRESS) {
997      Address so_addr;
998      StreamString error_strm;
999      SymbolContextList sc_list;
1000
1001      if (target->GetSectionLoadList().IsEmpty()) {
1002        // The target isn't loaded yet, we need to lookup the file address in
1003        // all modules
1004        const ModuleList &module_list = target->GetImages();
1005        const size_t num_modules = module_list.GetSize();
1006        for (size_t i = 0; i < num_modules; ++i) {
1007          ModuleSP module_sp(module_list.GetModuleAtIndex(i));
1008          if (module_sp &&
1009              module_sp->ResolveFileAddress(m_options.address, so_addr)) {
1010            SymbolContext sc;
1011            sc.Clear(true);
1012            if (module_sp->ResolveSymbolContextForAddress(
1013                    so_addr, eSymbolContextEverything, sc) &
1014                eSymbolContextLineEntry)
1015              sc_list.Append(sc);
1016          }
1017        }
1018
1019        if (sc_list.GetSize() == 0) {
1020          result.AppendErrorWithFormat(
1021              "no modules have source information for file address 0x%" PRIx64
1022              ".\n",
1023              m_options.address);
1024          result.SetStatus(eReturnStatusFailed);
1025          return false;
1026        }
1027      } else {
1028        // The target has some things loaded, resolve this address to a compile
1029        // unit + file + line and display
1030        if (target->GetSectionLoadList().ResolveLoadAddress(m_options.address,
1031                                                            so_addr)) {
1032          ModuleSP module_sp(so_addr.GetModule());
1033          if (module_sp) {
1034            SymbolContext sc;
1035            sc.Clear(true);
1036            if (module_sp->ResolveSymbolContextForAddress(
1037                    so_addr, eSymbolContextEverything, sc) &
1038                eSymbolContextLineEntry) {
1039              sc_list.Append(sc);
1040            } else {
1041              so_addr.Dump(&error_strm, nullptr,
1042                           Address::DumpStyleModuleWithFileAddress);
1043              result.AppendErrorWithFormat("address resolves to %s, but there "
1044                                           "is no line table information "
1045                                           "available for this address.\n",
1046                                           error_strm.GetData());
1047              result.SetStatus(eReturnStatusFailed);
1048              return false;
1049            }
1050          }
1051        }
1052
1053        if (sc_list.GetSize() == 0) {
1054          result.AppendErrorWithFormat(
1055              "no modules contain load address 0x%" PRIx64 ".\n",
1056              m_options.address);
1057          result.SetStatus(eReturnStatusFailed);
1058          return false;
1059        }
1060      }
1061      uint32_t num_matches = sc_list.GetSize();
1062      for (uint32_t i = 0; i < num_matches; ++i) {
1063        SymbolContext sc;
1064        sc_list.GetContextAtIndex(i, sc);
1065        if (sc.comp_unit) {
1066          if (m_options.show_bp_locs) {
1067            m_breakpoint_locations.Clear();
1068            const bool show_inlines = true;
1069            m_breakpoint_locations.Reset(sc.comp_unit->GetPrimaryFile(), 0,
1070                                         show_inlines);
1071            SearchFilterForUnconstrainedSearches target_search_filter(
1072                target->shared_from_this());
1073            target_search_filter.Search(m_breakpoint_locations);
1074          }
1075
1076          bool show_fullpaths = true;
1077          bool show_module = true;
1078          bool show_inlined_frames = true;
1079          const bool show_function_arguments = true;
1080          const bool show_function_name = true;
1081          sc.DumpStopContext(&result.GetOutputStream(),
1082                             m_exe_ctx.GetBestExecutionContextScope(),
1083                             sc.line_entry.range.GetBaseAddress(),
1084                             show_fullpaths, show_module, show_inlined_frames,
1085                             show_function_arguments, show_function_name);
1086          result.GetOutputStream().EOL();
1087
1088          if (m_options.num_lines == 0)
1089            m_options.num_lines = 10;
1090
1091          size_t lines_to_back_up =
1092              m_options.num_lines >= 10 ? 5 : m_options.num_lines / 2;
1093
1094          const uint32_t column =
1095              (GetDebugger().GetStopShowColumn() != eStopShowColumnNone)
1096                  ? sc.line_entry.column
1097                  : 0;
1098          target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
1099              sc.comp_unit->GetPrimaryFile(), sc.line_entry.line, column,
1100              lines_to_back_up, m_options.num_lines - lines_to_back_up, "->",
1101              &result.GetOutputStream(), GetBreakpointLocations());
1102          result.SetStatus(eReturnStatusSuccessFinishResult);
1103        }
1104      }
1105    } else if (m_options.file_name.empty()) {
1106      // Last valid source manager context, or the current frame if no valid
1107      // last context in source manager. One little trick here, if you type the
1108      // exact same list command twice in a row, it is more likely because you
1109      // typed it once, then typed it again
1110      if (m_options.start_line == 0) {
1111        if (target->GetSourceManager().DisplayMoreWithLineNumbers(
1112                &result.GetOutputStream(), m_options.num_lines,
1113                m_options.reverse, GetBreakpointLocations())) {
1114          result.SetStatus(eReturnStatusSuccessFinishResult);
1115        }
1116      } else {
1117        if (m_options.num_lines == 0)
1118          m_options.num_lines = 10;
1119
1120        if (m_options.show_bp_locs) {
1121          SourceManager::FileSP last_file_sp(
1122              target->GetSourceManager().GetLastFile());
1123          if (last_file_sp) {
1124            const bool show_inlines = true;
1125            m_breakpoint_locations.Reset(last_file_sp->GetFileSpec(), 0,
1126                                         show_inlines);
1127            SearchFilterForUnconstrainedSearches target_search_filter(
1128                target->shared_from_this());
1129            target_search_filter.Search(m_breakpoint_locations);
1130          }
1131        } else
1132          m_breakpoint_locations.Clear();
1133
1134        const uint32_t column = 0;
1135        if (target->GetSourceManager()
1136                .DisplaySourceLinesWithLineNumbersUsingLastFile(
1137                    m_options.start_line, // Line to display
1138                    m_options.num_lines,  // Lines after line to
1139                    UINT32_MAX,           // Don't mark "line"
1140                    column,
1141                    "", // Don't mark "line"
1142                    &result.GetOutputStream(), GetBreakpointLocations())) {
1143          result.SetStatus(eReturnStatusSuccessFinishResult);
1144        }
1145      }
1146    } else {
1147      const char *filename = m_options.file_name.c_str();
1148
1149      bool check_inlines = false;
1150      SymbolContextList sc_list;
1151      size_t num_matches = 0;
1152
1153      if (!m_options.modules.empty()) {
1154        ModuleList matching_modules;
1155        for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) {
1156          FileSpec module_file_spec(m_options.modules[i]);
1157          if (module_file_spec) {
1158            ModuleSpec module_spec(module_file_spec);
1159            matching_modules.Clear();
1160            target->GetImages().FindModules(module_spec, matching_modules);
1161            num_matches += matching_modules.ResolveSymbolContextForFilePath(
1162                filename, 0, check_inlines,
1163                SymbolContextItem(eSymbolContextModule |
1164                                  eSymbolContextCompUnit),
1165                sc_list);
1166          }
1167        }
1168      } else {
1169        num_matches = target->GetImages().ResolveSymbolContextForFilePath(
1170            filename, 0, check_inlines,
1171            eSymbolContextModule | eSymbolContextCompUnit, sc_list);
1172      }
1173
1174      if (num_matches == 0) {
1175        result.AppendErrorWithFormat("Could not find source file \"%s\".\n",
1176                                     m_options.file_name.c_str());
1177        result.SetStatus(eReturnStatusFailed);
1178        return false;
1179      }
1180
1181      if (num_matches > 1) {
1182        bool got_multiple = false;
1183        CompileUnit *test_cu = nullptr;
1184
1185        for (unsigned i = 0; i < num_matches; i++) {
1186          SymbolContext sc;
1187          sc_list.GetContextAtIndex(i, sc);
1188          if (sc.comp_unit) {
1189            if (test_cu) {
1190              if (test_cu != sc.comp_unit)
1191                got_multiple = true;
1192              break;
1193            } else
1194              test_cu = sc.comp_unit;
1195          }
1196        }
1197        if (got_multiple) {
1198          result.AppendErrorWithFormat(
1199              "Multiple source files found matching: \"%s.\"\n",
1200              m_options.file_name.c_str());
1201          result.SetStatus(eReturnStatusFailed);
1202          return false;
1203        }
1204      }
1205
1206      SymbolContext sc;
1207      if (sc_list.GetContextAtIndex(0, sc)) {
1208        if (sc.comp_unit) {
1209          if (m_options.show_bp_locs) {
1210            const bool show_inlines = true;
1211            m_breakpoint_locations.Reset(sc.comp_unit->GetPrimaryFile(), 0,
1212                                         show_inlines);
1213            SearchFilterForUnconstrainedSearches target_search_filter(
1214                target->shared_from_this());
1215            target_search_filter.Search(m_breakpoint_locations);
1216          } else
1217            m_breakpoint_locations.Clear();
1218
1219          if (m_options.num_lines == 0)
1220            m_options.num_lines = 10;
1221          const uint32_t column = 0;
1222          target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
1223              sc.comp_unit->GetPrimaryFile(), m_options.start_line, column, 0,
1224              m_options.num_lines, "", &result.GetOutputStream(),
1225              GetBreakpointLocations());
1226
1227          result.SetStatus(eReturnStatusSuccessFinishResult);
1228        } else {
1229          result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n",
1230                                       m_options.file_name.c_str());
1231          result.SetStatus(eReturnStatusFailed);
1232          return false;
1233        }
1234      }
1235    }
1236    return result.Succeeded();
1237  }
1238
1239  const SymbolContextList *GetBreakpointLocations() {
1240    if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0)
1241      return &m_breakpoint_locations.GetFileLineMatches();
1242    return nullptr;
1243  }
1244
1245  CommandOptions m_options;
1246  FileLineResolver m_breakpoint_locations;
1247  std::string m_reverse_name;
1248};
1249
1250#pragma mark CommandObjectMultiwordSource
1251// CommandObjectMultiwordSource
1252
1253CommandObjectMultiwordSource::CommandObjectMultiwordSource(
1254    CommandInterpreter &interpreter)
1255    : CommandObjectMultiword(interpreter, "source",
1256                             "Commands for examining "
1257                             "source code described by "
1258                             "debug information for the "
1259                             "current target process.",
1260                             "source <subcommand> [<subcommand-options>]") {
1261  LoadSubCommand("info",
1262                 CommandObjectSP(new CommandObjectSourceInfo(interpreter)));
1263  LoadSubCommand("list",
1264                 CommandObjectSP(new CommandObjectSourceList(interpreter)));
1265}
1266
1267CommandObjectMultiwordSource::~CommandObjectMultiwordSource() = default;
1268