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