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