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