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