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