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