xref: /openbsd-src/gnu/llvm/lldb/source/Commands/CommandObjectSource.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1dda28197Spatrick //===-- CommandObjectSource.cpp -------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "CommandObjectSource.h"
10061da546Spatrick 
11061da546Spatrick #include "lldb/Core/Debugger.h"
12061da546Spatrick #include "lldb/Core/FileLineResolver.h"
13061da546Spatrick #include "lldb/Core/Module.h"
14061da546Spatrick #include "lldb/Core/ModuleSpec.h"
15061da546Spatrick #include "lldb/Core/SourceManager.h"
16061da546Spatrick #include "lldb/Host/OptionParser.h"
17*f6aab3d8Srobert #include "lldb/Interpreter/CommandOptionArgumentTable.h"
18061da546Spatrick #include "lldb/Interpreter/CommandReturnObject.h"
19061da546Spatrick #include "lldb/Interpreter/OptionArgParser.h"
20be691f3bSpatrick #include "lldb/Interpreter/OptionValueFileColonLine.h"
21061da546Spatrick #include "lldb/Interpreter/Options.h"
22061da546Spatrick #include "lldb/Symbol/CompileUnit.h"
23061da546Spatrick #include "lldb/Symbol/Function.h"
24061da546Spatrick #include "lldb/Symbol/Symbol.h"
25061da546Spatrick #include "lldb/Target/SectionLoadList.h"
26061da546Spatrick #include "lldb/Target/StackFrame.h"
27061da546Spatrick #include "lldb/Utility/FileSpec.h"
28*f6aab3d8Srobert #include <optional>
29061da546Spatrick 
30061da546Spatrick using namespace lldb;
31061da546Spatrick using namespace lldb_private;
32061da546Spatrick 
33061da546Spatrick #pragma mark CommandObjectSourceInfo
34061da546Spatrick // CommandObjectSourceInfo - debug line entries dumping command
35061da546Spatrick #define LLDB_OPTIONS_source_info
36061da546Spatrick #include "CommandOptions.inc"
37061da546Spatrick 
38061da546Spatrick class CommandObjectSourceInfo : public CommandObjectParsed {
39061da546Spatrick   class CommandOptions : public Options {
40061da546Spatrick   public:
41*f6aab3d8Srobert     CommandOptions() = default;
42061da546Spatrick 
43061da546Spatrick     ~CommandOptions() override = default;
44061da546Spatrick 
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)45061da546Spatrick     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
46061da546Spatrick                           ExecutionContext *execution_context) override {
47061da546Spatrick       Status error;
48061da546Spatrick       const int short_option = GetDefinitions()[option_idx].short_option;
49061da546Spatrick       switch (short_option) {
50061da546Spatrick       case 'l':
51061da546Spatrick         if (option_arg.getAsInteger(0, start_line))
52061da546Spatrick           error.SetErrorStringWithFormat("invalid line number: '%s'",
53061da546Spatrick                                          option_arg.str().c_str());
54061da546Spatrick         break;
55061da546Spatrick 
56061da546Spatrick       case 'e':
57061da546Spatrick         if (option_arg.getAsInteger(0, end_line))
58061da546Spatrick           error.SetErrorStringWithFormat("invalid line number: '%s'",
59061da546Spatrick                                          option_arg.str().c_str());
60061da546Spatrick         break;
61061da546Spatrick 
62061da546Spatrick       case 'c':
63061da546Spatrick         if (option_arg.getAsInteger(0, num_lines))
64061da546Spatrick           error.SetErrorStringWithFormat("invalid line count: '%s'",
65061da546Spatrick                                          option_arg.str().c_str());
66061da546Spatrick         break;
67061da546Spatrick 
68061da546Spatrick       case 'f':
69dda28197Spatrick         file_name = std::string(option_arg);
70061da546Spatrick         break;
71061da546Spatrick 
72061da546Spatrick       case 'n':
73dda28197Spatrick         symbol_name = std::string(option_arg);
74061da546Spatrick         break;
75061da546Spatrick 
76061da546Spatrick       case 'a': {
77061da546Spatrick         address = OptionArgParser::ToAddress(execution_context, option_arg,
78061da546Spatrick                                              LLDB_INVALID_ADDRESS, &error);
79061da546Spatrick       } break;
80061da546Spatrick       case 's':
81061da546Spatrick         modules.push_back(std::string(option_arg));
82061da546Spatrick         break;
83061da546Spatrick       default:
84061da546Spatrick         llvm_unreachable("Unimplemented option");
85061da546Spatrick       }
86061da546Spatrick 
87061da546Spatrick       return error;
88061da546Spatrick     }
89061da546Spatrick 
OptionParsingStarting(ExecutionContext * execution_context)90061da546Spatrick     void OptionParsingStarting(ExecutionContext *execution_context) override {
91061da546Spatrick       file_spec.Clear();
92061da546Spatrick       file_name.clear();
93061da546Spatrick       symbol_name.clear();
94061da546Spatrick       address = LLDB_INVALID_ADDRESS;
95061da546Spatrick       start_line = 0;
96061da546Spatrick       end_line = 0;
97061da546Spatrick       num_lines = 0;
98061da546Spatrick       modules.clear();
99061da546Spatrick     }
100061da546Spatrick 
GetDefinitions()101061da546Spatrick     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
102*f6aab3d8Srobert       return llvm::ArrayRef(g_source_info_options);
103061da546Spatrick     }
104061da546Spatrick 
105061da546Spatrick     // Instance variables to hold the values for command options.
106061da546Spatrick     FileSpec file_spec;
107061da546Spatrick     std::string file_name;
108061da546Spatrick     std::string symbol_name;
109061da546Spatrick     lldb::addr_t address;
110061da546Spatrick     uint32_t start_line;
111061da546Spatrick     uint32_t end_line;
112061da546Spatrick     uint32_t num_lines;
113061da546Spatrick     std::vector<std::string> modules;
114061da546Spatrick   };
115061da546Spatrick 
116061da546Spatrick public:
CommandObjectSourceInfo(CommandInterpreter & interpreter)117061da546Spatrick   CommandObjectSourceInfo(CommandInterpreter &interpreter)
118061da546Spatrick       : CommandObjectParsed(
119061da546Spatrick             interpreter, "source info",
120061da546Spatrick             "Display source line information for the current target "
121061da546Spatrick             "process.  Defaults to instruction pointer in current stack "
122061da546Spatrick             "frame.",
123*f6aab3d8Srobert             nullptr, eCommandRequiresTarget) {}
124061da546Spatrick 
125061da546Spatrick   ~CommandObjectSourceInfo() override = default;
126061da546Spatrick 
GetOptions()127061da546Spatrick   Options *GetOptions() override { return &m_options; }
128061da546Spatrick 
129061da546Spatrick protected:
130061da546Spatrick   // Dump the line entries in each symbol context. Return the number of entries
131061da546Spatrick   // found. If module_list is set, only dump lines contained in one of the
132061da546Spatrick   // modules. If file_spec is set, only dump lines in the file. If the
133061da546Spatrick   // start_line option was specified, don't print lines less than start_line.
134061da546Spatrick   // If the end_line option was specified, don't print lines greater than
135061da546Spatrick   // end_line. If the num_lines option was specified, dont print more than
136061da546Spatrick   // num_lines entries.
DumpLinesInSymbolContexts(Stream & strm,const SymbolContextList & sc_list,const ModuleList & module_list,const FileSpec & file_spec)137061da546Spatrick   uint32_t DumpLinesInSymbolContexts(Stream &strm,
138061da546Spatrick                                      const SymbolContextList &sc_list,
139061da546Spatrick                                      const ModuleList &module_list,
140061da546Spatrick                                      const FileSpec &file_spec) {
141061da546Spatrick     uint32_t start_line = m_options.start_line;
142061da546Spatrick     uint32_t end_line = m_options.end_line;
143061da546Spatrick     uint32_t num_lines = m_options.num_lines;
144061da546Spatrick     Target *target = m_exe_ctx.GetTargetPtr();
145061da546Spatrick 
146061da546Spatrick     uint32_t num_matches = 0;
147061da546Spatrick     // Dump all the line entries for the file in the list.
148061da546Spatrick     ConstString last_module_file_name;
149061da546Spatrick     uint32_t num_scs = sc_list.GetSize();
150061da546Spatrick     for (uint32_t i = 0; i < num_scs; ++i) {
151061da546Spatrick       SymbolContext sc;
152061da546Spatrick       sc_list.GetContextAtIndex(i, sc);
153061da546Spatrick       if (sc.comp_unit) {
154061da546Spatrick         Module *module = sc.module_sp.get();
155061da546Spatrick         CompileUnit *cu = sc.comp_unit;
156061da546Spatrick         const LineEntry &line_entry = sc.line_entry;
157061da546Spatrick         assert(module && cu);
158061da546Spatrick 
159061da546Spatrick         // Are we looking for specific modules, files or lines?
160061da546Spatrick         if (module_list.GetSize() &&
161061da546Spatrick             module_list.GetIndexForModule(module) == LLDB_INVALID_INDEX32)
162061da546Spatrick           continue;
163061da546Spatrick         if (!FileSpec::Match(file_spec, line_entry.file))
164061da546Spatrick           continue;
165061da546Spatrick         if (start_line > 0 && line_entry.line < start_line)
166061da546Spatrick           continue;
167061da546Spatrick         if (end_line > 0 && line_entry.line > end_line)
168061da546Spatrick           continue;
169061da546Spatrick         if (num_lines > 0 && num_matches > num_lines)
170061da546Spatrick           continue;
171061da546Spatrick 
172061da546Spatrick         // Print a new header if the module changed.
173061da546Spatrick         ConstString module_file_name = module->GetFileSpec().GetFilename();
174061da546Spatrick         assert(module_file_name);
175061da546Spatrick         if (module_file_name != last_module_file_name) {
176061da546Spatrick           if (num_matches > 0)
177061da546Spatrick             strm << "\n\n";
178061da546Spatrick           strm << "Lines found in module `" << module_file_name << "\n";
179061da546Spatrick         }
180061da546Spatrick         // Dump the line entry.
181061da546Spatrick         line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu,
182061da546Spatrick                                   target, /*show_address_only=*/false);
183061da546Spatrick         strm << "\n";
184061da546Spatrick         last_module_file_name = module_file_name;
185061da546Spatrick         num_matches++;
186061da546Spatrick       }
187061da546Spatrick     }
188061da546Spatrick     return num_matches;
189061da546Spatrick   }
190061da546Spatrick 
191061da546Spatrick   // Dump the requested line entries for the file in the compilation unit.
192061da546Spatrick   // Return the number of entries found. If module_list is set, only dump lines
193061da546Spatrick   // contained in one of the modules. If the start_line option was specified,
194061da546Spatrick   // don't print lines less than start_line. If the end_line option was
195061da546Spatrick   // specified, don't print lines greater than end_line. If the num_lines
196061da546Spatrick   // option was specified, dont print more than num_lines entries.
DumpFileLinesInCompUnit(Stream & strm,Module * module,CompileUnit * cu,const FileSpec & file_spec)197061da546Spatrick   uint32_t DumpFileLinesInCompUnit(Stream &strm, Module *module,
198061da546Spatrick                                    CompileUnit *cu, const FileSpec &file_spec) {
199061da546Spatrick     uint32_t start_line = m_options.start_line;
200061da546Spatrick     uint32_t end_line = m_options.end_line;
201061da546Spatrick     uint32_t num_lines = m_options.num_lines;
202061da546Spatrick     Target *target = m_exe_ctx.GetTargetPtr();
203061da546Spatrick 
204061da546Spatrick     uint32_t num_matches = 0;
205061da546Spatrick     assert(module);
206061da546Spatrick     if (cu) {
207061da546Spatrick       assert(file_spec.GetFilename().AsCString());
208061da546Spatrick       bool has_path = (file_spec.GetDirectory().AsCString() != nullptr);
209061da546Spatrick       const FileSpecList &cu_file_list = cu->GetSupportFiles();
210061da546Spatrick       size_t file_idx = cu_file_list.FindFileIndex(0, file_spec, has_path);
211061da546Spatrick       if (file_idx != UINT32_MAX) {
212061da546Spatrick         // Update the file to how it appears in the CU.
213061da546Spatrick         const FileSpec &cu_file_spec =
214061da546Spatrick             cu_file_list.GetFileSpecAtIndex(file_idx);
215061da546Spatrick 
216061da546Spatrick         // Dump all matching lines at or above start_line for the file in the
217061da546Spatrick         // CU.
218061da546Spatrick         ConstString file_spec_name = file_spec.GetFilename();
219061da546Spatrick         ConstString module_file_name = module->GetFileSpec().GetFilename();
220061da546Spatrick         bool cu_header_printed = false;
221061da546Spatrick         uint32_t line = start_line;
222061da546Spatrick         while (true) {
223061da546Spatrick           LineEntry line_entry;
224061da546Spatrick 
225061da546Spatrick           // Find the lowest index of a line entry with a line equal to or
226061da546Spatrick           // higher than 'line'.
227061da546Spatrick           uint32_t start_idx = 0;
228061da546Spatrick           start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec,
229061da546Spatrick                                         /*exact=*/false, &line_entry);
230061da546Spatrick           if (start_idx == UINT32_MAX)
231061da546Spatrick             // No more line entries for our file in this CU.
232061da546Spatrick             break;
233061da546Spatrick 
234061da546Spatrick           if (end_line > 0 && line_entry.line > end_line)
235061da546Spatrick             break;
236061da546Spatrick 
237061da546Spatrick           // Loop through to find any other entries for this line, dumping
238061da546Spatrick           // each.
239061da546Spatrick           line = line_entry.line;
240061da546Spatrick           do {
241061da546Spatrick             num_matches++;
242061da546Spatrick             if (num_lines > 0 && num_matches > num_lines)
243061da546Spatrick               break;
244061da546Spatrick             assert(cu_file_spec == line_entry.file);
245061da546Spatrick             if (!cu_header_printed) {
246061da546Spatrick               if (num_matches > 0)
247061da546Spatrick                 strm << "\n\n";
248061da546Spatrick               strm << "Lines found for file " << file_spec_name
249061da546Spatrick                    << " in compilation unit "
250061da546Spatrick                    << cu->GetPrimaryFile().GetFilename() << " in `"
251061da546Spatrick                    << module_file_name << "\n";
252061da546Spatrick               cu_header_printed = true;
253061da546Spatrick             }
254061da546Spatrick             line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu,
255061da546Spatrick                                       target, /*show_address_only=*/false);
256061da546Spatrick             strm << "\n";
257061da546Spatrick 
258061da546Spatrick             // Anymore after this one?
259061da546Spatrick             start_idx++;
260061da546Spatrick             start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec,
261061da546Spatrick                                           /*exact=*/true, &line_entry);
262061da546Spatrick           } while (start_idx != UINT32_MAX);
263061da546Spatrick 
264061da546Spatrick           // Try the next higher line, starting over at start_idx 0.
265061da546Spatrick           line++;
266061da546Spatrick         }
267061da546Spatrick       }
268061da546Spatrick     }
269061da546Spatrick     return num_matches;
270061da546Spatrick   }
271061da546Spatrick 
272061da546Spatrick   // Dump the requested line entries for the file in the module. Return the
273061da546Spatrick   // number of entries found. If module_list is set, only dump lines contained
274061da546Spatrick   // in one of the modules. If the start_line option was specified, don't print
275061da546Spatrick   // lines less than start_line. If the end_line option was specified, don't
276061da546Spatrick   // print lines greater than end_line. If the num_lines option was specified,
277061da546Spatrick   // dont print more than num_lines entries.
DumpFileLinesInModule(Stream & strm,Module * module,const FileSpec & file_spec)278061da546Spatrick   uint32_t DumpFileLinesInModule(Stream &strm, Module *module,
279061da546Spatrick                                  const FileSpec &file_spec) {
280061da546Spatrick     uint32_t num_matches = 0;
281061da546Spatrick     if (module) {
282061da546Spatrick       // Look through all the compilation units (CUs) in this module for ones
283061da546Spatrick       // that contain lines of code from this source file.
284061da546Spatrick       for (size_t i = 0; i < module->GetNumCompileUnits(); i++) {
285061da546Spatrick         // Look for a matching source file in this CU.
286061da546Spatrick         CompUnitSP cu_sp(module->GetCompileUnitAtIndex(i));
287061da546Spatrick         if (cu_sp) {
288061da546Spatrick           num_matches +=
289061da546Spatrick               DumpFileLinesInCompUnit(strm, module, cu_sp.get(), file_spec);
290061da546Spatrick         }
291061da546Spatrick       }
292061da546Spatrick     }
293061da546Spatrick     return num_matches;
294061da546Spatrick   }
295061da546Spatrick 
296061da546Spatrick   // Given an address and a list of modules, append the symbol contexts of all
297061da546Spatrick   // line entries containing the address found in the modules and return the
298061da546Spatrick   // count of matches.  If none is found, return an error in 'error_strm'.
GetSymbolContextsForAddress(const ModuleList & module_list,lldb::addr_t addr,SymbolContextList & sc_list,StreamString & error_strm)299061da546Spatrick   size_t GetSymbolContextsForAddress(const ModuleList &module_list,
300061da546Spatrick                                      lldb::addr_t addr,
301061da546Spatrick                                      SymbolContextList &sc_list,
302061da546Spatrick                                      StreamString &error_strm) {
303061da546Spatrick     Address so_addr;
304061da546Spatrick     size_t num_matches = 0;
305061da546Spatrick     assert(module_list.GetSize() > 0);
306061da546Spatrick     Target *target = m_exe_ctx.GetTargetPtr();
307061da546Spatrick     if (target->GetSectionLoadList().IsEmpty()) {
308061da546Spatrick       // The target isn't loaded yet, we need to lookup the file address in all
309061da546Spatrick       // modules.  Note: the module list option does not apply to addresses.
310061da546Spatrick       const size_t num_modules = module_list.GetSize();
311061da546Spatrick       for (size_t i = 0; i < num_modules; ++i) {
312061da546Spatrick         ModuleSP module_sp(module_list.GetModuleAtIndex(i));
313061da546Spatrick         if (!module_sp)
314061da546Spatrick           continue;
315061da546Spatrick         if (module_sp->ResolveFileAddress(addr, so_addr)) {
316061da546Spatrick           SymbolContext sc;
317061da546Spatrick           sc.Clear(true);
318061da546Spatrick           if (module_sp->ResolveSymbolContextForAddress(
319061da546Spatrick                   so_addr, eSymbolContextEverything, sc) &
320061da546Spatrick               eSymbolContextLineEntry) {
321061da546Spatrick             sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false);
322061da546Spatrick             ++num_matches;
323061da546Spatrick           }
324061da546Spatrick         }
325061da546Spatrick       }
326061da546Spatrick       if (num_matches == 0)
327061da546Spatrick         error_strm.Printf("Source information for file address 0x%" PRIx64
328061da546Spatrick                           " not found in any modules.\n",
329061da546Spatrick                           addr);
330061da546Spatrick     } else {
331061da546Spatrick       // The target has some things loaded, resolve this address to a compile
332061da546Spatrick       // unit + file + line and display
333061da546Spatrick       if (target->GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) {
334061da546Spatrick         ModuleSP module_sp(so_addr.GetModule());
335061da546Spatrick         // Check to make sure this module is in our list.
336061da546Spatrick         if (module_sp && module_list.GetIndexForModule(module_sp.get()) !=
337061da546Spatrick                              LLDB_INVALID_INDEX32) {
338061da546Spatrick           SymbolContext sc;
339061da546Spatrick           sc.Clear(true);
340061da546Spatrick           if (module_sp->ResolveSymbolContextForAddress(
341061da546Spatrick                   so_addr, eSymbolContextEverything, sc) &
342061da546Spatrick               eSymbolContextLineEntry) {
343061da546Spatrick             sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false);
344061da546Spatrick             ++num_matches;
345061da546Spatrick           } else {
346061da546Spatrick             StreamString addr_strm;
347061da546Spatrick             so_addr.Dump(&addr_strm, nullptr,
348061da546Spatrick                          Address::DumpStyleModuleWithFileAddress);
349061da546Spatrick             error_strm.Printf(
350061da546Spatrick                 "Address 0x%" PRIx64 " resolves to %s, but there is"
351061da546Spatrick                 " no source information available for this address.\n",
352061da546Spatrick                 addr, addr_strm.GetData());
353061da546Spatrick           }
354061da546Spatrick         } else {
355061da546Spatrick           StreamString addr_strm;
356061da546Spatrick           so_addr.Dump(&addr_strm, nullptr,
357061da546Spatrick                        Address::DumpStyleModuleWithFileAddress);
358061da546Spatrick           error_strm.Printf("Address 0x%" PRIx64
359061da546Spatrick                             " resolves to %s, but it cannot"
360061da546Spatrick                             " be found in any modules.\n",
361061da546Spatrick                             addr, addr_strm.GetData());
362061da546Spatrick         }
363061da546Spatrick       } else
364061da546Spatrick         error_strm.Printf("Unable to resolve address 0x%" PRIx64 ".\n", addr);
365061da546Spatrick     }
366061da546Spatrick     return num_matches;
367061da546Spatrick   }
368061da546Spatrick 
369061da546Spatrick   // Dump the line entries found in functions matching the name specified in
370061da546Spatrick   // the option.
DumpLinesInFunctions(CommandReturnObject & result)371061da546Spatrick   bool DumpLinesInFunctions(CommandReturnObject &result) {
372061da546Spatrick     SymbolContextList sc_list_funcs;
373061da546Spatrick     ConstString name(m_options.symbol_name.c_str());
374061da546Spatrick     SymbolContextList sc_list_lines;
375061da546Spatrick     Target *target = m_exe_ctx.GetTargetPtr();
376061da546Spatrick     uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
377061da546Spatrick 
378*f6aab3d8Srobert     ModuleFunctionSearchOptions function_options;
379*f6aab3d8Srobert     function_options.include_symbols = false;
380*f6aab3d8Srobert     function_options.include_inlines = true;
381*f6aab3d8Srobert 
382061da546Spatrick     // Note: module_list can't be const& because FindFunctionSymbols isn't
383061da546Spatrick     // const.
384061da546Spatrick     ModuleList module_list =
385061da546Spatrick         (m_module_list.GetSize() > 0) ? m_module_list : target->GetImages();
386*f6aab3d8Srobert     module_list.FindFunctions(name, eFunctionNameTypeAuto, function_options,
387*f6aab3d8Srobert                               sc_list_funcs);
388061da546Spatrick     size_t num_matches = sc_list_funcs.GetSize();
389061da546Spatrick 
390061da546Spatrick     if (!num_matches) {
391061da546Spatrick       // If we didn't find any functions with that name, try searching for
392061da546Spatrick       // symbols that line up exactly with function addresses.
393061da546Spatrick       SymbolContextList sc_list_symbols;
394061da546Spatrick       module_list.FindFunctionSymbols(name, eFunctionNameTypeAuto,
395061da546Spatrick                                       sc_list_symbols);
396061da546Spatrick       size_t num_symbol_matches = sc_list_symbols.GetSize();
397061da546Spatrick       for (size_t i = 0; i < num_symbol_matches; i++) {
398061da546Spatrick         SymbolContext sc;
399061da546Spatrick         sc_list_symbols.GetContextAtIndex(i, sc);
400061da546Spatrick         if (sc.symbol && sc.symbol->ValueIsAddress()) {
401061da546Spatrick           const Address &base_address = sc.symbol->GetAddressRef();
402061da546Spatrick           Function *function = base_address.CalculateSymbolContextFunction();
403061da546Spatrick           if (function) {
404061da546Spatrick             sc_list_funcs.Append(SymbolContext(function));
405061da546Spatrick             num_matches++;
406061da546Spatrick           }
407061da546Spatrick         }
408061da546Spatrick       }
409061da546Spatrick     }
410061da546Spatrick     if (num_matches == 0) {
411061da546Spatrick       result.AppendErrorWithFormat("Could not find function named \'%s\'.\n",
412061da546Spatrick                                    m_options.symbol_name.c_str());
413061da546Spatrick       return false;
414061da546Spatrick     }
415061da546Spatrick     for (size_t i = 0; i < num_matches; i++) {
416061da546Spatrick       SymbolContext sc;
417061da546Spatrick       sc_list_funcs.GetContextAtIndex(i, sc);
418061da546Spatrick       bool context_found_for_symbol = false;
419061da546Spatrick       // Loop through all the ranges in the function.
420061da546Spatrick       AddressRange range;
421061da546Spatrick       for (uint32_t r = 0;
422061da546Spatrick            sc.GetAddressRange(eSymbolContextEverything, r,
423061da546Spatrick                               /*use_inline_block_range=*/true, range);
424061da546Spatrick            ++r) {
425061da546Spatrick         // Append the symbol contexts for each address in the range to
426061da546Spatrick         // sc_list_lines.
427061da546Spatrick         const Address &base_address = range.GetBaseAddress();
428061da546Spatrick         const addr_t size = range.GetByteSize();
429061da546Spatrick         lldb::addr_t start_addr = base_address.GetLoadAddress(target);
430061da546Spatrick         if (start_addr == LLDB_INVALID_ADDRESS)
431061da546Spatrick           start_addr = base_address.GetFileAddress();
432061da546Spatrick         lldb::addr_t end_addr = start_addr + size;
433061da546Spatrick         for (lldb::addr_t addr = start_addr; addr < end_addr;
434061da546Spatrick              addr += addr_byte_size) {
435061da546Spatrick           StreamString error_strm;
436061da546Spatrick           if (!GetSymbolContextsForAddress(module_list, addr, sc_list_lines,
437061da546Spatrick                                            error_strm))
438061da546Spatrick             result.AppendWarningWithFormat("in symbol '%s': %s",
439061da546Spatrick                                            sc.GetFunctionName().AsCString(),
440061da546Spatrick                                            error_strm.GetData());
441061da546Spatrick           else
442061da546Spatrick             context_found_for_symbol = true;
443061da546Spatrick         }
444061da546Spatrick       }
445061da546Spatrick       if (!context_found_for_symbol)
446061da546Spatrick         result.AppendWarningWithFormat("Unable to find line information"
447061da546Spatrick                                        " for matching symbol '%s'.\n",
448061da546Spatrick                                        sc.GetFunctionName().AsCString());
449061da546Spatrick     }
450061da546Spatrick     if (sc_list_lines.GetSize() == 0) {
451061da546Spatrick       result.AppendErrorWithFormat("No line information could be found"
452061da546Spatrick                                    " for any symbols matching '%s'.\n",
453061da546Spatrick                                    name.AsCString());
454061da546Spatrick       return false;
455061da546Spatrick     }
456061da546Spatrick     FileSpec file_spec;
457061da546Spatrick     if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list_lines,
458061da546Spatrick                                    module_list, file_spec)) {
459061da546Spatrick       result.AppendErrorWithFormat(
460061da546Spatrick           "Unable to dump line information for symbol '%s'.\n",
461061da546Spatrick           name.AsCString());
462061da546Spatrick       return false;
463061da546Spatrick     }
464061da546Spatrick     return true;
465061da546Spatrick   }
466061da546Spatrick 
467061da546Spatrick   // Dump the line entries found for the address specified in the option.
DumpLinesForAddress(CommandReturnObject & result)468061da546Spatrick   bool DumpLinesForAddress(CommandReturnObject &result) {
469061da546Spatrick     Target *target = m_exe_ctx.GetTargetPtr();
470061da546Spatrick     SymbolContextList sc_list;
471061da546Spatrick 
472061da546Spatrick     StreamString error_strm;
473061da546Spatrick     if (!GetSymbolContextsForAddress(target->GetImages(), m_options.address,
474061da546Spatrick                                      sc_list, error_strm)) {
475061da546Spatrick       result.AppendErrorWithFormat("%s.\n", error_strm.GetData());
476061da546Spatrick       return false;
477061da546Spatrick     }
478061da546Spatrick     ModuleList module_list;
479061da546Spatrick     FileSpec file_spec;
480061da546Spatrick     if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list,
481061da546Spatrick                                    module_list, file_spec)) {
482061da546Spatrick       result.AppendErrorWithFormat("No modules contain load address 0x%" PRIx64
483061da546Spatrick                                    ".\n",
484061da546Spatrick                                    m_options.address);
485061da546Spatrick       return false;
486061da546Spatrick     }
487061da546Spatrick     return true;
488061da546Spatrick   }
489061da546Spatrick 
490061da546Spatrick   // Dump the line entries found in the file specified in the option.
DumpLinesForFile(CommandReturnObject & result)491061da546Spatrick   bool DumpLinesForFile(CommandReturnObject &result) {
492061da546Spatrick     FileSpec file_spec(m_options.file_name);
493061da546Spatrick     const char *filename = m_options.file_name.c_str();
494061da546Spatrick     Target *target = m_exe_ctx.GetTargetPtr();
495061da546Spatrick     const ModuleList &module_list =
496061da546Spatrick         (m_module_list.GetSize() > 0) ? m_module_list : target->GetImages();
497061da546Spatrick 
498061da546Spatrick     bool displayed_something = false;
499061da546Spatrick     const size_t num_modules = module_list.GetSize();
500061da546Spatrick     for (uint32_t i = 0; i < num_modules; ++i) {
501061da546Spatrick       // Dump lines for this module.
502061da546Spatrick       Module *module = module_list.GetModulePointerAtIndex(i);
503061da546Spatrick       assert(module);
504061da546Spatrick       if (DumpFileLinesInModule(result.GetOutputStream(), module, file_spec))
505061da546Spatrick         displayed_something = true;
506061da546Spatrick     }
507061da546Spatrick     if (!displayed_something) {
508061da546Spatrick       result.AppendErrorWithFormat("No source filenames matched '%s'.\n",
509061da546Spatrick                                    filename);
510061da546Spatrick       return false;
511061da546Spatrick     }
512061da546Spatrick     return true;
513061da546Spatrick   }
514061da546Spatrick 
515061da546Spatrick   // Dump the line entries for the current frame.
DumpLinesForFrame(CommandReturnObject & result)516061da546Spatrick   bool DumpLinesForFrame(CommandReturnObject &result) {
517061da546Spatrick     StackFrame *cur_frame = m_exe_ctx.GetFramePtr();
518061da546Spatrick     if (cur_frame == nullptr) {
519061da546Spatrick       result.AppendError(
520061da546Spatrick           "No selected frame to use to find the default source.");
521061da546Spatrick       return false;
522061da546Spatrick     } else if (!cur_frame->HasDebugInformation()) {
523061da546Spatrick       result.AppendError("No debug info for the selected frame.");
524061da546Spatrick       return false;
525061da546Spatrick     } else {
526061da546Spatrick       const SymbolContext &sc =
527061da546Spatrick           cur_frame->GetSymbolContext(eSymbolContextLineEntry);
528061da546Spatrick       SymbolContextList sc_list;
529061da546Spatrick       sc_list.Append(sc);
530061da546Spatrick       ModuleList module_list;
531061da546Spatrick       FileSpec file_spec;
532061da546Spatrick       if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list,
533061da546Spatrick                                      module_list, file_spec)) {
534061da546Spatrick         result.AppendError(
535061da546Spatrick             "No source line info available for the selected frame.");
536061da546Spatrick         return false;
537061da546Spatrick       }
538061da546Spatrick     }
539061da546Spatrick     return true;
540061da546Spatrick   }
541061da546Spatrick 
DoExecute(Args & command,CommandReturnObject & result)542061da546Spatrick   bool DoExecute(Args &command, CommandReturnObject &result) override {
543061da546Spatrick     Target *target = m_exe_ctx.GetTargetPtr();
544061da546Spatrick     if (target == nullptr) {
545061da546Spatrick       target = GetDebugger().GetSelectedTarget().get();
546061da546Spatrick       if (target == nullptr) {
547061da546Spatrick         result.AppendError("invalid target, create a debug target using the "
548061da546Spatrick                            "'target create' command.");
549061da546Spatrick         return false;
550061da546Spatrick       }
551061da546Spatrick     }
552061da546Spatrick 
553061da546Spatrick     uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
554061da546Spatrick     result.GetOutputStream().SetAddressByteSize(addr_byte_size);
555061da546Spatrick     result.GetErrorStream().SetAddressByteSize(addr_byte_size);
556061da546Spatrick 
557061da546Spatrick     // Collect the list of modules to search.
558061da546Spatrick     m_module_list.Clear();
559061da546Spatrick     if (!m_options.modules.empty()) {
560061da546Spatrick       for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) {
561061da546Spatrick         FileSpec module_file_spec(m_options.modules[i]);
562061da546Spatrick         if (module_file_spec) {
563061da546Spatrick           ModuleSpec module_spec(module_file_spec);
564061da546Spatrick           target->GetImages().FindModules(module_spec, m_module_list);
565061da546Spatrick           if (m_module_list.IsEmpty())
566061da546Spatrick             result.AppendWarningWithFormat("No module found for '%s'.\n",
567061da546Spatrick                                            m_options.modules[i].c_str());
568061da546Spatrick         }
569061da546Spatrick       }
570061da546Spatrick       if (!m_module_list.GetSize()) {
571061da546Spatrick         result.AppendError("No modules match the input.");
572061da546Spatrick         return false;
573061da546Spatrick       }
574061da546Spatrick     } else if (target->GetImages().GetSize() == 0) {
575061da546Spatrick       result.AppendError("The target has no associated executable images.");
576061da546Spatrick       return false;
577061da546Spatrick     }
578061da546Spatrick 
579061da546Spatrick     // Check the arguments to see what lines we should dump.
580061da546Spatrick     if (!m_options.symbol_name.empty()) {
581061da546Spatrick       // Print lines for symbol.
582061da546Spatrick       if (DumpLinesInFunctions(result))
583061da546Spatrick         result.SetStatus(eReturnStatusSuccessFinishResult);
584061da546Spatrick       else
585061da546Spatrick         result.SetStatus(eReturnStatusFailed);
586061da546Spatrick     } else if (m_options.address != LLDB_INVALID_ADDRESS) {
587061da546Spatrick       // Print lines for an address.
588061da546Spatrick       if (DumpLinesForAddress(result))
589061da546Spatrick         result.SetStatus(eReturnStatusSuccessFinishResult);
590061da546Spatrick       else
591061da546Spatrick         result.SetStatus(eReturnStatusFailed);
592061da546Spatrick     } else if (!m_options.file_name.empty()) {
593061da546Spatrick       // Dump lines for a file.
594061da546Spatrick       if (DumpLinesForFile(result))
595061da546Spatrick         result.SetStatus(eReturnStatusSuccessFinishResult);
596061da546Spatrick       else
597061da546Spatrick         result.SetStatus(eReturnStatusFailed);
598061da546Spatrick     } else {
599061da546Spatrick       // Dump the line for the current frame.
600061da546Spatrick       if (DumpLinesForFrame(result))
601061da546Spatrick         result.SetStatus(eReturnStatusSuccessFinishResult);
602061da546Spatrick       else
603061da546Spatrick         result.SetStatus(eReturnStatusFailed);
604061da546Spatrick     }
605061da546Spatrick     return result.Succeeded();
606061da546Spatrick   }
607061da546Spatrick 
608061da546Spatrick   CommandOptions m_options;
609061da546Spatrick   ModuleList m_module_list;
610061da546Spatrick };
611061da546Spatrick 
612061da546Spatrick #pragma mark CommandObjectSourceList
613061da546Spatrick // CommandObjectSourceList
614061da546Spatrick #define LLDB_OPTIONS_source_list
615061da546Spatrick #include "CommandOptions.inc"
616061da546Spatrick 
617061da546Spatrick class CommandObjectSourceList : public CommandObjectParsed {
618061da546Spatrick   class CommandOptions : public Options {
619061da546Spatrick   public:
620*f6aab3d8Srobert     CommandOptions() = default;
621061da546Spatrick 
622061da546Spatrick     ~CommandOptions() override = default;
623061da546Spatrick 
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)624061da546Spatrick     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
625061da546Spatrick                           ExecutionContext *execution_context) override {
626061da546Spatrick       Status error;
627061da546Spatrick       const int short_option = GetDefinitions()[option_idx].short_option;
628061da546Spatrick       switch (short_option) {
629061da546Spatrick       case 'l':
630061da546Spatrick         if (option_arg.getAsInteger(0, start_line))
631061da546Spatrick           error.SetErrorStringWithFormat("invalid line number: '%s'",
632061da546Spatrick                                          option_arg.str().c_str());
633061da546Spatrick         break;
634061da546Spatrick 
635061da546Spatrick       case 'c':
636061da546Spatrick         if (option_arg.getAsInteger(0, num_lines))
637061da546Spatrick           error.SetErrorStringWithFormat("invalid line count: '%s'",
638061da546Spatrick                                          option_arg.str().c_str());
639061da546Spatrick         break;
640061da546Spatrick 
641061da546Spatrick       case 'f':
642dda28197Spatrick         file_name = std::string(option_arg);
643061da546Spatrick         break;
644061da546Spatrick 
645061da546Spatrick       case 'n':
646dda28197Spatrick         symbol_name = std::string(option_arg);
647061da546Spatrick         break;
648061da546Spatrick 
649061da546Spatrick       case 'a': {
650061da546Spatrick         address = OptionArgParser::ToAddress(execution_context, option_arg,
651061da546Spatrick                                              LLDB_INVALID_ADDRESS, &error);
652061da546Spatrick       } break;
653061da546Spatrick       case 's':
654061da546Spatrick         modules.push_back(std::string(option_arg));
655061da546Spatrick         break;
656061da546Spatrick 
657061da546Spatrick       case 'b':
658061da546Spatrick         show_bp_locs = true;
659061da546Spatrick         break;
660061da546Spatrick       case 'r':
661061da546Spatrick         reverse = true;
662061da546Spatrick         break;
663be691f3bSpatrick       case 'y':
664be691f3bSpatrick       {
665be691f3bSpatrick         OptionValueFileColonLine value;
666be691f3bSpatrick         Status fcl_err = value.SetValueFromString(option_arg);
667be691f3bSpatrick         if (!fcl_err.Success()) {
668be691f3bSpatrick           error.SetErrorStringWithFormat(
669be691f3bSpatrick               "Invalid value for file:line specifier: %s",
670be691f3bSpatrick               fcl_err.AsCString());
671be691f3bSpatrick         } else {
672be691f3bSpatrick           file_name = value.GetFileSpec().GetPath();
673be691f3bSpatrick           start_line = value.GetLineNumber();
674be691f3bSpatrick           // I don't see anything useful to do with a column number, but I don't
675be691f3bSpatrick           // want to complain since someone may well have cut and pasted a
676be691f3bSpatrick           // listing from somewhere that included a column.
677be691f3bSpatrick         }
678be691f3bSpatrick       } break;
679061da546Spatrick       default:
680061da546Spatrick         llvm_unreachable("Unimplemented option");
681061da546Spatrick       }
682061da546Spatrick 
683061da546Spatrick       return error;
684061da546Spatrick     }
685061da546Spatrick 
OptionParsingStarting(ExecutionContext * execution_context)686061da546Spatrick     void OptionParsingStarting(ExecutionContext *execution_context) override {
687061da546Spatrick       file_spec.Clear();
688061da546Spatrick       file_name.clear();
689061da546Spatrick       symbol_name.clear();
690061da546Spatrick       address = LLDB_INVALID_ADDRESS;
691061da546Spatrick       start_line = 0;
692061da546Spatrick       num_lines = 0;
693061da546Spatrick       show_bp_locs = false;
694061da546Spatrick       reverse = false;
695061da546Spatrick       modules.clear();
696061da546Spatrick     }
697061da546Spatrick 
GetDefinitions()698061da546Spatrick     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
699*f6aab3d8Srobert       return llvm::ArrayRef(g_source_list_options);
700061da546Spatrick     }
701061da546Spatrick 
702061da546Spatrick     // Instance variables to hold the values for command options.
703061da546Spatrick     FileSpec file_spec;
704061da546Spatrick     std::string file_name;
705061da546Spatrick     std::string symbol_name;
706061da546Spatrick     lldb::addr_t address;
707061da546Spatrick     uint32_t start_line;
708061da546Spatrick     uint32_t num_lines;
709061da546Spatrick     std::vector<std::string> modules;
710061da546Spatrick     bool show_bp_locs;
711061da546Spatrick     bool reverse;
712061da546Spatrick   };
713061da546Spatrick 
714061da546Spatrick public:
CommandObjectSourceList(CommandInterpreter & interpreter)715061da546Spatrick   CommandObjectSourceList(CommandInterpreter &interpreter)
716061da546Spatrick       : CommandObjectParsed(interpreter, "source list",
717061da546Spatrick                             "Display source code for the current target "
718061da546Spatrick                             "process as specified by options.",
719*f6aab3d8Srobert                             nullptr, eCommandRequiresTarget) {}
720061da546Spatrick 
721061da546Spatrick   ~CommandObjectSourceList() override = default;
722061da546Spatrick 
GetOptions()723061da546Spatrick   Options *GetOptions() override { return &m_options; }
724061da546Spatrick 
GetRepeatCommand(Args & current_command_args,uint32_t index)725*f6aab3d8Srobert   std::optional<std::string> GetRepeatCommand(Args &current_command_args,
726061da546Spatrick                                               uint32_t index) override {
727061da546Spatrick     // This is kind of gross, but the command hasn't been parsed yet so we
728061da546Spatrick     // can't look at the option values for this invocation...  I have to scan
729061da546Spatrick     // the arguments directly.
730061da546Spatrick     auto iter =
731061da546Spatrick         llvm::find_if(current_command_args, [](const Args::ArgEntry &e) {
732061da546Spatrick           return e.ref() == "-r" || e.ref() == "--reverse";
733061da546Spatrick         });
734061da546Spatrick     if (iter == current_command_args.end())
735*f6aab3d8Srobert       return m_cmd_name;
736061da546Spatrick 
737061da546Spatrick     if (m_reverse_name.empty()) {
738061da546Spatrick       m_reverse_name = m_cmd_name;
739061da546Spatrick       m_reverse_name.append(" -r");
740061da546Spatrick     }
741*f6aab3d8Srobert     return m_reverse_name;
742061da546Spatrick   }
743061da546Spatrick 
744061da546Spatrick protected:
745061da546Spatrick   struct SourceInfo {
746061da546Spatrick     ConstString function;
747061da546Spatrick     LineEntry line_entry;
748061da546Spatrick 
SourceInfoCommandObjectSourceList::SourceInfo749061da546Spatrick     SourceInfo(ConstString name, const LineEntry &line_entry)
750061da546Spatrick         : function(name), line_entry(line_entry) {}
751061da546Spatrick 
752*f6aab3d8Srobert     SourceInfo() = default;
753061da546Spatrick 
IsValidCommandObjectSourceList::SourceInfo754061da546Spatrick     bool IsValid() const { return (bool)function && line_entry.IsValid(); }
755061da546Spatrick 
operator ==CommandObjectSourceList::SourceInfo756061da546Spatrick     bool operator==(const SourceInfo &rhs) const {
757061da546Spatrick       return function == rhs.function &&
758061da546Spatrick              line_entry.original_file == rhs.line_entry.original_file &&
759061da546Spatrick              line_entry.line == rhs.line_entry.line;
760061da546Spatrick     }
761061da546Spatrick 
operator !=CommandObjectSourceList::SourceInfo762061da546Spatrick     bool operator!=(const SourceInfo &rhs) const {
763061da546Spatrick       return function != rhs.function ||
764061da546Spatrick              line_entry.original_file != rhs.line_entry.original_file ||
765061da546Spatrick              line_entry.line != rhs.line_entry.line;
766061da546Spatrick     }
767061da546Spatrick 
operator <CommandObjectSourceList::SourceInfo768061da546Spatrick     bool operator<(const SourceInfo &rhs) const {
769061da546Spatrick       if (function.GetCString() < rhs.function.GetCString())
770061da546Spatrick         return true;
771061da546Spatrick       if (line_entry.file.GetDirectory().GetCString() <
772061da546Spatrick           rhs.line_entry.file.GetDirectory().GetCString())
773061da546Spatrick         return true;
774061da546Spatrick       if (line_entry.file.GetFilename().GetCString() <
775061da546Spatrick           rhs.line_entry.file.GetFilename().GetCString())
776061da546Spatrick         return true;
777061da546Spatrick       if (line_entry.line < rhs.line_entry.line)
778061da546Spatrick         return true;
779061da546Spatrick       return false;
780061da546Spatrick     }
781061da546Spatrick   };
782061da546Spatrick 
DisplayFunctionSource(const SymbolContext & sc,SourceInfo & source_info,CommandReturnObject & result)783061da546Spatrick   size_t DisplayFunctionSource(const SymbolContext &sc, SourceInfo &source_info,
784061da546Spatrick                                CommandReturnObject &result) {
785061da546Spatrick     if (!source_info.IsValid()) {
786061da546Spatrick       source_info.function = sc.GetFunctionName();
787061da546Spatrick       source_info.line_entry = sc.GetFunctionStartLineEntry();
788061da546Spatrick     }
789061da546Spatrick 
790061da546Spatrick     if (sc.function) {
791061da546Spatrick       Target *target = m_exe_ctx.GetTargetPtr();
792061da546Spatrick 
793061da546Spatrick       FileSpec start_file;
794061da546Spatrick       uint32_t start_line;
795061da546Spatrick       uint32_t end_line;
796061da546Spatrick       FileSpec end_file;
797061da546Spatrick 
798061da546Spatrick       if (sc.block == nullptr) {
799061da546Spatrick         // Not an inlined function
800061da546Spatrick         sc.function->GetStartLineSourceInfo(start_file, start_line);
801061da546Spatrick         if (start_line == 0) {
802061da546Spatrick           result.AppendErrorWithFormat("Could not find line information for "
803061da546Spatrick                                        "start of function: \"%s\".\n",
804061da546Spatrick                                        source_info.function.GetCString());
805061da546Spatrick           return 0;
806061da546Spatrick         }
807061da546Spatrick         sc.function->GetEndLineSourceInfo(end_file, end_line);
808061da546Spatrick       } else {
809061da546Spatrick         // We have an inlined function
810061da546Spatrick         start_file = source_info.line_entry.file;
811061da546Spatrick         start_line = source_info.line_entry.line;
812061da546Spatrick         end_line = start_line + m_options.num_lines;
813061da546Spatrick       }
814061da546Spatrick 
815061da546Spatrick       // This is a little hacky, but the first line table entry for a function
816061da546Spatrick       // points to the "{" that starts the function block.  It would be nice to
817061da546Spatrick       // actually get the function declaration in there too.  So back up a bit,
818061da546Spatrick       // but not further than what you're going to display.
819061da546Spatrick       uint32_t extra_lines;
820061da546Spatrick       if (m_options.num_lines >= 10)
821061da546Spatrick         extra_lines = 5;
822061da546Spatrick       else
823061da546Spatrick         extra_lines = m_options.num_lines / 2;
824061da546Spatrick       uint32_t line_no;
825061da546Spatrick       if (start_line <= extra_lines)
826061da546Spatrick         line_no = 1;
827061da546Spatrick       else
828061da546Spatrick         line_no = start_line - extra_lines;
829061da546Spatrick 
830061da546Spatrick       // For fun, if the function is shorter than the number of lines we're
831061da546Spatrick       // supposed to display, only display the function...
832061da546Spatrick       if (end_line != 0) {
833061da546Spatrick         if (m_options.num_lines > end_line - line_no)
834061da546Spatrick           m_options.num_lines = end_line - line_no + extra_lines;
835061da546Spatrick       }
836061da546Spatrick 
837061da546Spatrick       m_breakpoint_locations.Clear();
838061da546Spatrick 
839061da546Spatrick       if (m_options.show_bp_locs) {
840061da546Spatrick         const bool show_inlines = true;
841061da546Spatrick         m_breakpoint_locations.Reset(start_file, 0, show_inlines);
842061da546Spatrick         SearchFilterForUnconstrainedSearches target_search_filter(
843061da546Spatrick             m_exe_ctx.GetTargetSP());
844061da546Spatrick         target_search_filter.Search(m_breakpoint_locations);
845061da546Spatrick       }
846061da546Spatrick 
847061da546Spatrick       result.AppendMessageWithFormat("File: %s\n",
848061da546Spatrick                                      start_file.GetPath().c_str());
849061da546Spatrick       // We don't care about the column here.
850061da546Spatrick       const uint32_t column = 0;
851061da546Spatrick       return target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
852061da546Spatrick           start_file, line_no, column, 0, m_options.num_lines, "",
853061da546Spatrick           &result.GetOutputStream(), GetBreakpointLocations());
854061da546Spatrick     } else {
855061da546Spatrick       result.AppendErrorWithFormat(
856061da546Spatrick           "Could not find function info for: \"%s\".\n",
857061da546Spatrick           m_options.symbol_name.c_str());
858061da546Spatrick     }
859061da546Spatrick     return 0;
860061da546Spatrick   }
861061da546Spatrick 
862061da546Spatrick   // From Jim: The FindMatchingFunctions / FindMatchingFunctionSymbols
863061da546Spatrick   // functions "take a possibly empty vector of strings which are names of
864061da546Spatrick   // modules, and run the two search functions on the subset of the full module
865061da546Spatrick   // list that matches the strings in the input vector". If we wanted to put
866061da546Spatrick   // these somewhere, there should probably be a module-filter-list that can be
867061da546Spatrick   // passed to the various ModuleList::Find* calls, which would either be a
868061da546Spatrick   // vector of string names or a ModuleSpecList.
FindMatchingFunctions(Target * target,ConstString name,SymbolContextList & sc_list)869061da546Spatrick   void FindMatchingFunctions(Target *target, ConstString name,
870061da546Spatrick                              SymbolContextList &sc_list) {
871061da546Spatrick     // Displaying the source for a symbol:
872061da546Spatrick     if (m_options.num_lines == 0)
873061da546Spatrick       m_options.num_lines = 10;
874061da546Spatrick 
875*f6aab3d8Srobert     ModuleFunctionSearchOptions function_options;
876*f6aab3d8Srobert     function_options.include_symbols = true;
877*f6aab3d8Srobert     function_options.include_inlines = false;
878*f6aab3d8Srobert 
879061da546Spatrick     const size_t num_modules = m_options.modules.size();
880061da546Spatrick     if (num_modules > 0) {
881061da546Spatrick       ModuleList matching_modules;
882061da546Spatrick       for (size_t i = 0; i < num_modules; ++i) {
883061da546Spatrick         FileSpec module_file_spec(m_options.modules[i]);
884061da546Spatrick         if (module_file_spec) {
885061da546Spatrick           ModuleSpec module_spec(module_file_spec);
886061da546Spatrick           matching_modules.Clear();
887061da546Spatrick           target->GetImages().FindModules(module_spec, matching_modules);
888*f6aab3d8Srobert 
889061da546Spatrick           matching_modules.FindFunctions(name, eFunctionNameTypeAuto,
890*f6aab3d8Srobert                                          function_options, sc_list);
891061da546Spatrick         }
892061da546Spatrick       }
893061da546Spatrick     } else {
894061da546Spatrick       target->GetImages().FindFunctions(name, eFunctionNameTypeAuto,
895*f6aab3d8Srobert                                         function_options, sc_list);
896061da546Spatrick     }
897061da546Spatrick   }
898061da546Spatrick 
FindMatchingFunctionSymbols(Target * target,ConstString name,SymbolContextList & sc_list)899061da546Spatrick   void FindMatchingFunctionSymbols(Target *target, ConstString name,
900061da546Spatrick                                    SymbolContextList &sc_list) {
901061da546Spatrick     const size_t num_modules = m_options.modules.size();
902061da546Spatrick     if (num_modules > 0) {
903061da546Spatrick       ModuleList matching_modules;
904061da546Spatrick       for (size_t i = 0; i < num_modules; ++i) {
905061da546Spatrick         FileSpec module_file_spec(m_options.modules[i]);
906061da546Spatrick         if (module_file_spec) {
907061da546Spatrick           ModuleSpec module_spec(module_file_spec);
908061da546Spatrick           matching_modules.Clear();
909061da546Spatrick           target->GetImages().FindModules(module_spec, matching_modules);
910061da546Spatrick           matching_modules.FindFunctionSymbols(name, eFunctionNameTypeAuto,
911061da546Spatrick                                                sc_list);
912061da546Spatrick         }
913061da546Spatrick       }
914061da546Spatrick     } else {
915061da546Spatrick       target->GetImages().FindFunctionSymbols(name, eFunctionNameTypeAuto,
916061da546Spatrick                                               sc_list);
917061da546Spatrick     }
918061da546Spatrick   }
919061da546Spatrick 
DoExecute(Args & command,CommandReturnObject & result)920061da546Spatrick   bool DoExecute(Args &command, CommandReturnObject &result) override {
921061da546Spatrick     Target *target = m_exe_ctx.GetTargetPtr();
922061da546Spatrick 
923061da546Spatrick     if (!m_options.symbol_name.empty()) {
924061da546Spatrick       SymbolContextList sc_list;
925061da546Spatrick       ConstString name(m_options.symbol_name.c_str());
926061da546Spatrick 
927061da546Spatrick       // Displaying the source for a symbol. Search for function named name.
928061da546Spatrick       FindMatchingFunctions(target, name, sc_list);
929061da546Spatrick       size_t num_matches = sc_list.GetSize();
930061da546Spatrick       if (!num_matches) {
931061da546Spatrick         // If we didn't find any functions with that name, try searching for
932061da546Spatrick         // symbols that line up exactly with function addresses.
933061da546Spatrick         SymbolContextList sc_list_symbols;
934061da546Spatrick         FindMatchingFunctionSymbols(target, name, sc_list_symbols);
935061da546Spatrick         size_t num_symbol_matches = sc_list_symbols.GetSize();
936061da546Spatrick 
937061da546Spatrick         for (size_t i = 0; i < num_symbol_matches; i++) {
938061da546Spatrick           SymbolContext sc;
939061da546Spatrick           sc_list_symbols.GetContextAtIndex(i, sc);
940061da546Spatrick           if (sc.symbol && sc.symbol->ValueIsAddress()) {
941061da546Spatrick             const Address &base_address = sc.symbol->GetAddressRef();
942061da546Spatrick             Function *function = base_address.CalculateSymbolContextFunction();
943061da546Spatrick             if (function) {
944061da546Spatrick               sc_list.Append(SymbolContext(function));
945061da546Spatrick               num_matches++;
946061da546Spatrick               break;
947061da546Spatrick             }
948061da546Spatrick           }
949061da546Spatrick         }
950061da546Spatrick       }
951061da546Spatrick 
952061da546Spatrick       if (num_matches == 0) {
953061da546Spatrick         result.AppendErrorWithFormat("Could not find function named: \"%s\".\n",
954061da546Spatrick                                      m_options.symbol_name.c_str());
955061da546Spatrick         return false;
956061da546Spatrick       }
957061da546Spatrick 
958061da546Spatrick       if (num_matches > 1) {
959061da546Spatrick         std::set<SourceInfo> source_match_set;
960061da546Spatrick 
961061da546Spatrick         bool displayed_something = false;
962061da546Spatrick         for (size_t i = 0; i < num_matches; i++) {
963061da546Spatrick           SymbolContext sc;
964061da546Spatrick           sc_list.GetContextAtIndex(i, sc);
965061da546Spatrick           SourceInfo source_info(sc.GetFunctionName(),
966061da546Spatrick                                  sc.GetFunctionStartLineEntry());
967061da546Spatrick 
968061da546Spatrick           if (source_info.IsValid()) {
969061da546Spatrick             if (source_match_set.find(source_info) == source_match_set.end()) {
970061da546Spatrick               source_match_set.insert(source_info);
971061da546Spatrick               if (DisplayFunctionSource(sc, source_info, result))
972061da546Spatrick                 displayed_something = true;
973061da546Spatrick             }
974061da546Spatrick           }
975061da546Spatrick         }
976061da546Spatrick 
977061da546Spatrick         if (displayed_something)
978061da546Spatrick           result.SetStatus(eReturnStatusSuccessFinishResult);
979061da546Spatrick         else
980061da546Spatrick           result.SetStatus(eReturnStatusFailed);
981061da546Spatrick       } else {
982061da546Spatrick         SymbolContext sc;
983061da546Spatrick         sc_list.GetContextAtIndex(0, sc);
984061da546Spatrick         SourceInfo source_info;
985061da546Spatrick 
986061da546Spatrick         if (DisplayFunctionSource(sc, source_info, result)) {
987061da546Spatrick           result.SetStatus(eReturnStatusSuccessFinishResult);
988061da546Spatrick         } else {
989061da546Spatrick           result.SetStatus(eReturnStatusFailed);
990061da546Spatrick         }
991061da546Spatrick       }
992061da546Spatrick       return result.Succeeded();
993061da546Spatrick     } else if (m_options.address != LLDB_INVALID_ADDRESS) {
994061da546Spatrick       Address so_addr;
995061da546Spatrick       StreamString error_strm;
996061da546Spatrick       SymbolContextList sc_list;
997061da546Spatrick 
998061da546Spatrick       if (target->GetSectionLoadList().IsEmpty()) {
999061da546Spatrick         // The target isn't loaded yet, we need to lookup the file address in
1000061da546Spatrick         // all modules
1001061da546Spatrick         const ModuleList &module_list = target->GetImages();
1002061da546Spatrick         const size_t num_modules = module_list.GetSize();
1003061da546Spatrick         for (size_t i = 0; i < num_modules; ++i) {
1004061da546Spatrick           ModuleSP module_sp(module_list.GetModuleAtIndex(i));
1005061da546Spatrick           if (module_sp &&
1006061da546Spatrick               module_sp->ResolveFileAddress(m_options.address, so_addr)) {
1007061da546Spatrick             SymbolContext sc;
1008061da546Spatrick             sc.Clear(true);
1009061da546Spatrick             if (module_sp->ResolveSymbolContextForAddress(
1010061da546Spatrick                     so_addr, eSymbolContextEverything, sc) &
1011061da546Spatrick                 eSymbolContextLineEntry)
1012061da546Spatrick               sc_list.Append(sc);
1013061da546Spatrick           }
1014061da546Spatrick         }
1015061da546Spatrick 
1016061da546Spatrick         if (sc_list.GetSize() == 0) {
1017061da546Spatrick           result.AppendErrorWithFormat(
1018061da546Spatrick               "no modules have source information for file address 0x%" PRIx64
1019061da546Spatrick               ".\n",
1020061da546Spatrick               m_options.address);
1021061da546Spatrick           return false;
1022061da546Spatrick         }
1023061da546Spatrick       } else {
1024061da546Spatrick         // The target has some things loaded, resolve this address to a compile
1025061da546Spatrick         // unit + file + line and display
1026061da546Spatrick         if (target->GetSectionLoadList().ResolveLoadAddress(m_options.address,
1027061da546Spatrick                                                             so_addr)) {
1028061da546Spatrick           ModuleSP module_sp(so_addr.GetModule());
1029061da546Spatrick           if (module_sp) {
1030061da546Spatrick             SymbolContext sc;
1031061da546Spatrick             sc.Clear(true);
1032061da546Spatrick             if (module_sp->ResolveSymbolContextForAddress(
1033061da546Spatrick                     so_addr, eSymbolContextEverything, sc) &
1034061da546Spatrick                 eSymbolContextLineEntry) {
1035061da546Spatrick               sc_list.Append(sc);
1036061da546Spatrick             } else {
1037061da546Spatrick               so_addr.Dump(&error_strm, nullptr,
1038061da546Spatrick                            Address::DumpStyleModuleWithFileAddress);
1039061da546Spatrick               result.AppendErrorWithFormat("address resolves to %s, but there "
1040061da546Spatrick                                            "is no line table information "
1041061da546Spatrick                                            "available for this address.\n",
1042061da546Spatrick                                            error_strm.GetData());
1043061da546Spatrick               return false;
1044061da546Spatrick             }
1045061da546Spatrick           }
1046061da546Spatrick         }
1047061da546Spatrick 
1048061da546Spatrick         if (sc_list.GetSize() == 0) {
1049061da546Spatrick           result.AppendErrorWithFormat(
1050061da546Spatrick               "no modules contain load address 0x%" PRIx64 ".\n",
1051061da546Spatrick               m_options.address);
1052061da546Spatrick           return false;
1053061da546Spatrick         }
1054061da546Spatrick       }
1055061da546Spatrick       uint32_t num_matches = sc_list.GetSize();
1056061da546Spatrick       for (uint32_t i = 0; i < num_matches; ++i) {
1057061da546Spatrick         SymbolContext sc;
1058061da546Spatrick         sc_list.GetContextAtIndex(i, sc);
1059061da546Spatrick         if (sc.comp_unit) {
1060061da546Spatrick           if (m_options.show_bp_locs) {
1061061da546Spatrick             m_breakpoint_locations.Clear();
1062061da546Spatrick             const bool show_inlines = true;
1063061da546Spatrick             m_breakpoint_locations.Reset(sc.comp_unit->GetPrimaryFile(), 0,
1064061da546Spatrick                                          show_inlines);
1065061da546Spatrick             SearchFilterForUnconstrainedSearches target_search_filter(
1066061da546Spatrick                 target->shared_from_this());
1067061da546Spatrick             target_search_filter.Search(m_breakpoint_locations);
1068061da546Spatrick           }
1069061da546Spatrick 
1070061da546Spatrick           bool show_fullpaths = true;
1071061da546Spatrick           bool show_module = true;
1072061da546Spatrick           bool show_inlined_frames = true;
1073061da546Spatrick           const bool show_function_arguments = true;
1074061da546Spatrick           const bool show_function_name = true;
1075061da546Spatrick           sc.DumpStopContext(&result.GetOutputStream(),
1076061da546Spatrick                              m_exe_ctx.GetBestExecutionContextScope(),
1077061da546Spatrick                              sc.line_entry.range.GetBaseAddress(),
1078061da546Spatrick                              show_fullpaths, show_module, show_inlined_frames,
1079061da546Spatrick                              show_function_arguments, show_function_name);
1080061da546Spatrick           result.GetOutputStream().EOL();
1081061da546Spatrick 
1082061da546Spatrick           if (m_options.num_lines == 0)
1083061da546Spatrick             m_options.num_lines = 10;
1084061da546Spatrick 
1085061da546Spatrick           size_t lines_to_back_up =
1086061da546Spatrick               m_options.num_lines >= 10 ? 5 : m_options.num_lines / 2;
1087061da546Spatrick 
1088061da546Spatrick           const uint32_t column =
1089061da546Spatrick               (GetDebugger().GetStopShowColumn() != eStopShowColumnNone)
1090061da546Spatrick                   ? sc.line_entry.column
1091061da546Spatrick                   : 0;
1092061da546Spatrick           target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
1093061da546Spatrick               sc.comp_unit->GetPrimaryFile(), sc.line_entry.line, column,
1094061da546Spatrick               lines_to_back_up, m_options.num_lines - lines_to_back_up, "->",
1095061da546Spatrick               &result.GetOutputStream(), GetBreakpointLocations());
1096061da546Spatrick           result.SetStatus(eReturnStatusSuccessFinishResult);
1097061da546Spatrick         }
1098061da546Spatrick       }
1099061da546Spatrick     } else if (m_options.file_name.empty()) {
1100061da546Spatrick       // Last valid source manager context, or the current frame if no valid
1101061da546Spatrick       // last context in source manager. One little trick here, if you type the
1102061da546Spatrick       // exact same list command twice in a row, it is more likely because you
1103061da546Spatrick       // typed it once, then typed it again
1104061da546Spatrick       if (m_options.start_line == 0) {
1105061da546Spatrick         if (target->GetSourceManager().DisplayMoreWithLineNumbers(
1106061da546Spatrick                 &result.GetOutputStream(), m_options.num_lines,
1107061da546Spatrick                 m_options.reverse, GetBreakpointLocations())) {
1108061da546Spatrick           result.SetStatus(eReturnStatusSuccessFinishResult);
1109061da546Spatrick         }
1110061da546Spatrick       } else {
1111061da546Spatrick         if (m_options.num_lines == 0)
1112061da546Spatrick           m_options.num_lines = 10;
1113061da546Spatrick 
1114061da546Spatrick         if (m_options.show_bp_locs) {
1115061da546Spatrick           SourceManager::FileSP last_file_sp(
1116061da546Spatrick               target->GetSourceManager().GetLastFile());
1117061da546Spatrick           if (last_file_sp) {
1118061da546Spatrick             const bool show_inlines = true;
1119061da546Spatrick             m_breakpoint_locations.Reset(last_file_sp->GetFileSpec(), 0,
1120061da546Spatrick                                          show_inlines);
1121061da546Spatrick             SearchFilterForUnconstrainedSearches target_search_filter(
1122061da546Spatrick                 target->shared_from_this());
1123061da546Spatrick             target_search_filter.Search(m_breakpoint_locations);
1124061da546Spatrick           }
1125061da546Spatrick         } else
1126061da546Spatrick           m_breakpoint_locations.Clear();
1127061da546Spatrick 
1128061da546Spatrick         const uint32_t column = 0;
1129061da546Spatrick         if (target->GetSourceManager()
1130061da546Spatrick                 .DisplaySourceLinesWithLineNumbersUsingLastFile(
1131061da546Spatrick                     m_options.start_line, // Line to display
1132061da546Spatrick                     m_options.num_lines,  // Lines after line to
1133061da546Spatrick                     UINT32_MAX,           // Don't mark "line"
1134061da546Spatrick                     column,
1135061da546Spatrick                     "", // Don't mark "line"
1136061da546Spatrick                     &result.GetOutputStream(), GetBreakpointLocations())) {
1137061da546Spatrick           result.SetStatus(eReturnStatusSuccessFinishResult);
1138061da546Spatrick         }
1139061da546Spatrick       }
1140061da546Spatrick     } else {
1141061da546Spatrick       const char *filename = m_options.file_name.c_str();
1142061da546Spatrick 
1143061da546Spatrick       bool check_inlines = false;
1144061da546Spatrick       SymbolContextList sc_list;
1145061da546Spatrick       size_t num_matches = 0;
1146061da546Spatrick 
1147061da546Spatrick       if (!m_options.modules.empty()) {
1148061da546Spatrick         ModuleList matching_modules;
1149061da546Spatrick         for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) {
1150061da546Spatrick           FileSpec module_file_spec(m_options.modules[i]);
1151061da546Spatrick           if (module_file_spec) {
1152061da546Spatrick             ModuleSpec module_spec(module_file_spec);
1153061da546Spatrick             matching_modules.Clear();
1154061da546Spatrick             target->GetImages().FindModules(module_spec, matching_modules);
1155061da546Spatrick             num_matches += matching_modules.ResolveSymbolContextForFilePath(
1156061da546Spatrick                 filename, 0, check_inlines,
1157061da546Spatrick                 SymbolContextItem(eSymbolContextModule |
1158061da546Spatrick                                   eSymbolContextCompUnit),
1159061da546Spatrick                 sc_list);
1160061da546Spatrick           }
1161061da546Spatrick         }
1162061da546Spatrick       } else {
1163061da546Spatrick         num_matches = target->GetImages().ResolveSymbolContextForFilePath(
1164061da546Spatrick             filename, 0, check_inlines,
1165061da546Spatrick             eSymbolContextModule | eSymbolContextCompUnit, sc_list);
1166061da546Spatrick       }
1167061da546Spatrick 
1168061da546Spatrick       if (num_matches == 0) {
1169061da546Spatrick         result.AppendErrorWithFormat("Could not find source file \"%s\".\n",
1170061da546Spatrick                                      m_options.file_name.c_str());
1171061da546Spatrick         return false;
1172061da546Spatrick       }
1173061da546Spatrick 
1174061da546Spatrick       if (num_matches > 1) {
1175061da546Spatrick         bool got_multiple = false;
1176061da546Spatrick         CompileUnit *test_cu = nullptr;
1177061da546Spatrick 
1178061da546Spatrick         for (unsigned i = 0; i < num_matches; i++) {
1179061da546Spatrick           SymbolContext sc;
1180061da546Spatrick           sc_list.GetContextAtIndex(i, sc);
1181061da546Spatrick           if (sc.comp_unit) {
1182061da546Spatrick             if (test_cu) {
1183061da546Spatrick               if (test_cu != sc.comp_unit)
1184061da546Spatrick                 got_multiple = true;
1185061da546Spatrick               break;
1186061da546Spatrick             } else
1187061da546Spatrick               test_cu = sc.comp_unit;
1188061da546Spatrick           }
1189061da546Spatrick         }
1190061da546Spatrick         if (got_multiple) {
1191061da546Spatrick           result.AppendErrorWithFormat(
1192061da546Spatrick               "Multiple source files found matching: \"%s.\"\n",
1193061da546Spatrick               m_options.file_name.c_str());
1194061da546Spatrick           return false;
1195061da546Spatrick         }
1196061da546Spatrick       }
1197061da546Spatrick 
1198061da546Spatrick       SymbolContext sc;
1199061da546Spatrick       if (sc_list.GetContextAtIndex(0, sc)) {
1200061da546Spatrick         if (sc.comp_unit) {
1201061da546Spatrick           if (m_options.show_bp_locs) {
1202061da546Spatrick             const bool show_inlines = true;
1203061da546Spatrick             m_breakpoint_locations.Reset(sc.comp_unit->GetPrimaryFile(), 0,
1204061da546Spatrick                                          show_inlines);
1205061da546Spatrick             SearchFilterForUnconstrainedSearches target_search_filter(
1206061da546Spatrick                 target->shared_from_this());
1207061da546Spatrick             target_search_filter.Search(m_breakpoint_locations);
1208061da546Spatrick           } else
1209061da546Spatrick             m_breakpoint_locations.Clear();
1210061da546Spatrick 
1211061da546Spatrick           if (m_options.num_lines == 0)
1212061da546Spatrick             m_options.num_lines = 10;
1213061da546Spatrick           const uint32_t column = 0;
1214061da546Spatrick           target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
1215061da546Spatrick               sc.comp_unit->GetPrimaryFile(), m_options.start_line, column, 0,
1216061da546Spatrick               m_options.num_lines, "", &result.GetOutputStream(),
1217061da546Spatrick               GetBreakpointLocations());
1218061da546Spatrick 
1219061da546Spatrick           result.SetStatus(eReturnStatusSuccessFinishResult);
1220061da546Spatrick         } else {
1221061da546Spatrick           result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n",
1222061da546Spatrick                                        m_options.file_name.c_str());
1223061da546Spatrick           return false;
1224061da546Spatrick         }
1225061da546Spatrick       }
1226061da546Spatrick     }
1227061da546Spatrick     return result.Succeeded();
1228061da546Spatrick   }
1229061da546Spatrick 
GetBreakpointLocations()1230061da546Spatrick   const SymbolContextList *GetBreakpointLocations() {
1231061da546Spatrick     if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0)
1232061da546Spatrick       return &m_breakpoint_locations.GetFileLineMatches();
1233061da546Spatrick     return nullptr;
1234061da546Spatrick   }
1235061da546Spatrick 
1236061da546Spatrick   CommandOptions m_options;
1237061da546Spatrick   FileLineResolver m_breakpoint_locations;
1238061da546Spatrick   std::string m_reverse_name;
1239061da546Spatrick };
1240061da546Spatrick 
1241061da546Spatrick #pragma mark CommandObjectMultiwordSource
1242061da546Spatrick // CommandObjectMultiwordSource
1243061da546Spatrick 
CommandObjectMultiwordSource(CommandInterpreter & interpreter)1244061da546Spatrick CommandObjectMultiwordSource::CommandObjectMultiwordSource(
1245061da546Spatrick     CommandInterpreter &interpreter)
1246061da546Spatrick     : CommandObjectMultiword(interpreter, "source",
1247061da546Spatrick                              "Commands for examining "
1248061da546Spatrick                              "source code described by "
1249061da546Spatrick                              "debug information for the "
1250061da546Spatrick                              "current target process.",
1251061da546Spatrick                              "source <subcommand> [<subcommand-options>]") {
1252061da546Spatrick   LoadSubCommand("info",
1253061da546Spatrick                  CommandObjectSP(new CommandObjectSourceInfo(interpreter)));
1254061da546Spatrick   LoadSubCommand("list",
1255061da546Spatrick                  CommandObjectSP(new CommandObjectSourceList(interpreter)));
1256061da546Spatrick }
1257061da546Spatrick 
1258061da546Spatrick CommandObjectMultiwordSource::~CommandObjectMultiwordSource() = default;
1259