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