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