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