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 = Status::FromErrorStringWithFormat(format: "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 = Status::FromErrorStringWithFormat(format: "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 = Status::FromErrorStringWithFormat(format: "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 = GetTarget(); |
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 = GetTarget(); |
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 cu_header_printed = 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: &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 = GetTarget(); |
305 | if (!target.HasLoadedSections()) { |
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.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 = GetTarget(); |
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 = GetTarget(); |
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 = GetTarget(); |
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 = GetTarget(); |
537 | |
538 | uint32_t addr_byte_size = target.GetArchitecture().GetAddressByteSize(); |
539 | result.GetOutputStream().SetAddressByteSize(addr_byte_size); |
540 | result.GetErrorStream().SetAddressByteSize(addr_byte_size); |
541 | |
542 | // Collect the list of modules to search. |
543 | m_module_list.Clear(); |
544 | if (!m_options.modules.empty()) { |
545 | for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) { |
546 | FileSpec module_file_spec(m_options.modules[i]); |
547 | if (module_file_spec) { |
548 | ModuleSpec module_spec(module_file_spec); |
549 | target.GetImages().FindModules(module_spec, matching_module_list&: m_module_list); |
550 | if (m_module_list.IsEmpty()) |
551 | result.AppendWarningWithFormat("No module found for '%s'.\n", |
552 | m_options.modules[i].c_str()); |
553 | } |
554 | } |
555 | if (!m_module_list.GetSize()) { |
556 | result.AppendError(in_string: "No modules match the input."); |
557 | return; |
558 | } |
559 | } else if (target.GetImages().GetSize() == 0) { |
560 | result.AppendError(in_string: "The target has no associated executable images."); |
561 | return; |
562 | } |
563 | |
564 | // Check the arguments to see what lines we should dump. |
565 | if (!m_options.symbol_name.empty()) { |
566 | // Print lines for symbol. |
567 | if (DumpLinesInFunctions(result)) |
568 | result.SetStatus(eReturnStatusSuccessFinishResult); |
569 | else |
570 | result.SetStatus(eReturnStatusFailed); |
571 | } else if (m_options.address != LLDB_INVALID_ADDRESS) { |
572 | // Print lines for an address. |
573 | if (DumpLinesForAddress(result)) |
574 | result.SetStatus(eReturnStatusSuccessFinishResult); |
575 | else |
576 | result.SetStatus(eReturnStatusFailed); |
577 | } else if (!m_options.file_name.empty()) { |
578 | // Dump lines for a file. |
579 | if (DumpLinesForFile(result)) |
580 | result.SetStatus(eReturnStatusSuccessFinishResult); |
581 | else |
582 | result.SetStatus(eReturnStatusFailed); |
583 | } else { |
584 | // Dump the line for the current frame. |
585 | if (DumpLinesForFrame(result)) |
586 | result.SetStatus(eReturnStatusSuccessFinishResult); |
587 | else |
588 | result.SetStatus(eReturnStatusFailed); |
589 | } |
590 | } |
591 | |
592 | CommandOptions m_options; |
593 | ModuleList m_module_list; |
594 | }; |
595 | |
596 | #pragma mark CommandObjectSourceList |
597 | // CommandObjectSourceList |
598 | #define LLDB_OPTIONS_source_list |
599 | #include "CommandOptions.inc" |
600 | |
601 | class CommandObjectSourceList : public CommandObjectParsed { |
602 | class CommandOptions : public Options { |
603 | public: |
604 | CommandOptions() = default; |
605 | |
606 | ~CommandOptions() override = default; |
607 | |
608 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
609 | ExecutionContext *execution_context) override { |
610 | Status error; |
611 | const int short_option = GetDefinitions()[option_idx].short_option; |
612 | switch (short_option) { |
613 | case 'l': |
614 | if (option_arg.getAsInteger(Radix: 0, Result&: start_line)) |
615 | error = Status::FromErrorStringWithFormat(format: "invalid line number: '%s'", |
616 | option_arg.str().c_str()); |
617 | break; |
618 | |
619 | case 'c': |
620 | if (option_arg.getAsInteger(Radix: 0, Result&: num_lines)) |
621 | error = Status::FromErrorStringWithFormat(format: "invalid line count: '%s'", |
622 | option_arg.str().c_str()); |
623 | break; |
624 | |
625 | case 'f': |
626 | file_name = std::string(option_arg); |
627 | break; |
628 | |
629 | case 'n': |
630 | symbol_name = std::string(option_arg); |
631 | break; |
632 | |
633 | case 'a': { |
634 | address = OptionArgParser::ToAddress(exe_ctx: execution_context, s: option_arg, |
635 | LLDB_INVALID_ADDRESS, error_ptr: &error); |
636 | } break; |
637 | case 's': |
638 | modules.push_back(x: std::string(option_arg)); |
639 | break; |
640 | |
641 | case 'b': |
642 | show_bp_locs = true; |
643 | break; |
644 | case 'r': |
645 | reverse = true; |
646 | break; |
647 | case 'y': |
648 | { |
649 | OptionValueFileColonLine value; |
650 | Status fcl_err = value.SetValueFromString(value: option_arg); |
651 | if (!fcl_err.Success()) { |
652 | error = Status::FromErrorStringWithFormat( |
653 | format: "Invalid value for file:line specifier: %s", fcl_err.AsCString()); |
654 | } else { |
655 | file_name = value.GetFileSpec().GetPath(); |
656 | start_line = value.GetLineNumber(); |
657 | // I don't see anything useful to do with a column number, but I don't |
658 | // want to complain since someone may well have cut and pasted a |
659 | // listing from somewhere that included a column. |
660 | } |
661 | } break; |
662 | default: |
663 | llvm_unreachable("Unimplemented option"); |
664 | } |
665 | |
666 | return error; |
667 | } |
668 | |
669 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
670 | file_spec.Clear(); |
671 | file_name.clear(); |
672 | symbol_name.clear(); |
673 | address = LLDB_INVALID_ADDRESS; |
674 | start_line = 0; |
675 | num_lines = 0; |
676 | show_bp_locs = false; |
677 | reverse = false; |
678 | modules.clear(); |
679 | } |
680 | |
681 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
682 | return llvm::ArrayRef(g_source_list_options); |
683 | } |
684 | |
685 | // Instance variables to hold the values for command options. |
686 | FileSpec file_spec; |
687 | std::string file_name; |
688 | std::string symbol_name; |
689 | lldb::addr_t address; |
690 | uint32_t start_line; |
691 | uint32_t num_lines; |
692 | std::vector<std::string> modules; |
693 | bool show_bp_locs; |
694 | bool reverse; |
695 | }; |
696 | |
697 | public: |
698 | CommandObjectSourceList(CommandInterpreter &interpreter) |
699 | : CommandObjectParsed(interpreter, "source list", |
700 | "Display source code for the current target " |
701 | "process as specified by options.", |
702 | nullptr, eCommandRequiresTarget) {} |
703 | |
704 | ~CommandObjectSourceList() override = default; |
705 | |
706 | Options *GetOptions() override { return &m_options; } |
707 | |
708 | std::optional<std::string> GetRepeatCommand(Args ¤t_command_args, |
709 | uint32_t index) override { |
710 | // This is kind of gross, but the command hasn't been parsed yet so we |
711 | // can't look at the option values for this invocation... I have to scan |
712 | // the arguments directly. |
713 | auto iter = |
714 | llvm::find_if(Range&: current_command_args, P: [](const Args::ArgEntry &e) { |
715 | return e.ref() == "-r"|| e.ref() == "--reverse"; |
716 | }); |
717 | if (iter == current_command_args.end()) |
718 | return m_cmd_name; |
719 | |
720 | if (m_reverse_name.empty()) { |
721 | m_reverse_name = m_cmd_name; |
722 | m_reverse_name.append(s: " -r"); |
723 | } |
724 | return m_reverse_name; |
725 | } |
726 | |
727 | protected: |
728 | struct SourceInfo { |
729 | ConstString function; |
730 | LineEntry line_entry; |
731 | |
732 | SourceInfo(ConstString name, const LineEntry &line_entry) |
733 | : function(name), line_entry(line_entry) {} |
734 | |
735 | SourceInfo() = default; |
736 | |
737 | bool IsValid() const { return (bool)function && line_entry.IsValid(); } |
738 | |
739 | bool operator==(const SourceInfo &rhs) const { |
740 | return function == rhs.function && |
741 | line_entry.original_file_sp->Equal( |
742 | other: *rhs.line_entry.original_file_sp, |
743 | equality: SupportFile::eEqualFileSpecAndChecksumIfSet) && |
744 | line_entry.line == rhs.line_entry.line; |
745 | } |
746 | |
747 | bool operator!=(const SourceInfo &rhs) const { |
748 | return function != rhs.function || |
749 | !line_entry.original_file_sp->Equal( |
750 | other: *rhs.line_entry.original_file_sp, |
751 | equality: SupportFile::eEqualFileSpecAndChecksumIfSet) || |
752 | line_entry.line != rhs.line_entry.line; |
753 | } |
754 | |
755 | bool operator<(const SourceInfo &rhs) const { |
756 | if (function.GetCString() < rhs.function.GetCString()) |
757 | return true; |
758 | if (line_entry.GetFile().GetDirectory().GetCString() < |
759 | rhs.line_entry.GetFile().GetDirectory().GetCString()) |
760 | return true; |
761 | if (line_entry.GetFile().GetFilename().GetCString() < |
762 | rhs.line_entry.GetFile().GetFilename().GetCString()) |
763 | return true; |
764 | if (line_entry.line < rhs.line_entry.line) |
765 | return true; |
766 | return false; |
767 | } |
768 | }; |
769 | |
770 | size_t DisplayFunctionSource(const SymbolContext &sc, SourceInfo &source_info, |
771 | CommandReturnObject &result) { |
772 | if (!source_info.IsValid()) { |
773 | source_info.function = sc.GetFunctionName(); |
774 | source_info.line_entry = sc.GetFunctionStartLineEntry(); |
775 | } |
776 | |
777 | if (sc.function) { |
778 | Target &target = GetTarget(); |
779 | |
780 | SupportFileSP start_file = std::make_shared<SupportFile>(); |
781 | uint32_t start_line; |
782 | uint32_t end_line; |
783 | FileSpec end_file; |
784 | |
785 | if (sc.block == nullptr) { |
786 | // Not an inlined function |
787 | auto expected_info = sc.function->GetSourceInfo(); |
788 | if (!expected_info) { |
789 | result.AppendError(in_string: llvm::toString(E: expected_info.takeError())); |
790 | return 0; |
791 | } |
792 | start_file = expected_info->first; |
793 | start_line = expected_info->second.GetRangeBase(); |
794 | end_line = expected_info->second.GetRangeEnd(); |
795 | } else { |
796 | // We have an inlined function |
797 | start_file = source_info.line_entry.file_sp; |
798 | start_line = source_info.line_entry.line; |
799 | end_line = start_line + m_options.num_lines; |
800 | } |
801 | |
802 | // This is a little hacky, but the first line table entry for a function |
803 | // points to the "{" that starts the function block. It would be nice to |
804 | // actually get the function declaration in there too. So back up a bit, |
805 | // but not further than what you're going to display. |
806 | uint32_t extra_lines; |
807 | if (m_options.num_lines >= 10) |
808 | extra_lines = 5; |
809 | else |
810 | extra_lines = m_options.num_lines / 2; |
811 | uint32_t line_no; |
812 | if (start_line <= extra_lines) |
813 | line_no = 1; |
814 | else |
815 | line_no = start_line - extra_lines; |
816 | |
817 | // For fun, if the function is shorter than the number of lines we're |
818 | // supposed to display, only display the function... |
819 | if (end_line != 0) { |
820 | if (m_options.num_lines > end_line - line_no) |
821 | m_options.num_lines = end_line - line_no + extra_lines; |
822 | } |
823 | |
824 | m_breakpoint_locations.Clear(); |
825 | |
826 | if (m_options.show_bp_locs) { |
827 | const bool show_inlines = true; |
828 | m_breakpoint_locations.Reset(file_spec: start_file->GetSpecOnly(), line: 0, |
829 | check_inlines: show_inlines); |
830 | SearchFilterForUnconstrainedSearches target_search_filter( |
831 | m_exe_ctx.GetTargetSP()); |
832 | target_search_filter.Search(searcher&: m_breakpoint_locations); |
833 | } |
834 | |
835 | result.AppendMessageWithFormat( |
836 | format: "File: %s\n", start_file->GetSpecOnly().GetPath().c_str()); |
837 | // We don't care about the column here. |
838 | const uint32_t column = 0; |
839 | return target.GetSourceManager().DisplaySourceLinesWithLineNumbers( |
840 | support_file_sp: start_file, line: line_no, column, context_before: 0, context_after: m_options.num_lines, current_line_cstr: "", |
841 | s: &result.GetOutputStream(), bp_locs: GetBreakpointLocations()); |
842 | } else { |
843 | result.AppendErrorWithFormat( |
844 | format: "Could not find function info for: \"%s\".\n", |
845 | m_options.symbol_name.c_str()); |
846 | } |
847 | return 0; |
848 | } |
849 | |
850 | // From Jim: The FindMatchingFunctions / FindMatchingFunctionSymbols |
851 | // functions "take a possibly empty vector of strings which are names of |
852 | // modules, and run the two search functions on the subset of the full module |
853 | // list that matches the strings in the input vector". If we wanted to put |
854 | // these somewhere, there should probably be a module-filter-list that can be |
855 | // passed to the various ModuleList::Find* calls, which would either be a |
856 | // vector of string names or a ModuleSpecList. |
857 | void FindMatchingFunctions(Target &target, ConstString name, |
858 | SymbolContextList &sc_list) { |
859 | // Displaying the source for a symbol: |
860 | if (m_options.num_lines == 0) |
861 | m_options.num_lines = 10; |
862 | |
863 | ModuleFunctionSearchOptions function_options; |
864 | function_options.include_symbols = true; |
865 | function_options.include_inlines = false; |
866 | |
867 | const size_t num_modules = m_options.modules.size(); |
868 | if (num_modules > 0) { |
869 | ModuleList matching_modules; |
870 | for (size_t i = 0; i < num_modules; ++i) { |
871 | FileSpec module_file_spec(m_options.modules[i]); |
872 | if (module_file_spec) { |
873 | ModuleSpec module_spec(module_file_spec); |
874 | matching_modules.Clear(); |
875 | target.GetImages().FindModules(module_spec, matching_module_list&: matching_modules); |
876 | |
877 | matching_modules.FindFunctions(name, name_type_mask: eFunctionNameTypeAuto, |
878 | options: function_options, sc_list); |
879 | } |
880 | } |
881 | } else { |
882 | target.GetImages().FindFunctions(name, name_type_mask: eFunctionNameTypeAuto, |
883 | options: function_options, sc_list); |
884 | } |
885 | } |
886 | |
887 | void FindMatchingFunctionSymbols(Target &target, ConstString name, |
888 | SymbolContextList &sc_list) { |
889 | const size_t num_modules = m_options.modules.size(); |
890 | if (num_modules > 0) { |
891 | ModuleList matching_modules; |
892 | for (size_t i = 0; i < num_modules; ++i) { |
893 | FileSpec module_file_spec(m_options.modules[i]); |
894 | if (module_file_spec) { |
895 | ModuleSpec module_spec(module_file_spec); |
896 | matching_modules.Clear(); |
897 | target.GetImages().FindModules(module_spec, matching_module_list&: matching_modules); |
898 | matching_modules.FindFunctionSymbols(name, name_type_mask: eFunctionNameTypeAuto, |
899 | sc_list); |
900 | } |
901 | } |
902 | } else { |
903 | target.GetImages().FindFunctionSymbols(name, name_type_mask: eFunctionNameTypeAuto, |
904 | sc_list); |
905 | } |
906 | } |
907 | |
908 | void DoExecute(Args &command, CommandReturnObject &result) override { |
909 | Target &target = GetTarget(); |
910 | |
911 | if (!m_options.symbol_name.empty()) { |
912 | SymbolContextList sc_list; |
913 | ConstString name(m_options.symbol_name.c_str()); |
914 | |
915 | // Displaying the source for a symbol. Search for function named name. |
916 | FindMatchingFunctions(target, name, sc_list); |
917 | if (sc_list.GetSize() == 0) { |
918 | // If we didn't find any functions with that name, try searching for |
919 | // symbols that line up exactly with function addresses. |
920 | SymbolContextList sc_list_symbols; |
921 | FindMatchingFunctionSymbols(target, name, sc_list&: sc_list_symbols); |
922 | for (const SymbolContext &sc : sc_list_symbols) { |
923 | if (sc.symbol && sc.symbol->ValueIsAddress()) { |
924 | const Address &base_address = sc.symbol->GetAddressRef(); |
925 | Function *function = base_address.CalculateSymbolContextFunction(); |
926 | if (function) { |
927 | sc_list.Append(sc: SymbolContext(function)); |
928 | break; |
929 | } |
930 | } |
931 | } |
932 | } |
933 | |
934 | if (sc_list.GetSize() == 0) { |
935 | result.AppendErrorWithFormat(format: "Could not find function named: \"%s\".\n", |
936 | m_options.symbol_name.c_str()); |
937 | return; |
938 | } |
939 | |
940 | std::set<SourceInfo> source_match_set; |
941 | bool displayed_something = false; |
942 | for (const SymbolContext &sc : sc_list) { |
943 | SourceInfo source_info(sc.GetFunctionName(), |
944 | sc.GetFunctionStartLineEntry()); |
945 | if (source_info.IsValid() && |
946 | source_match_set.find(x: source_info) == source_match_set.end()) { |
947 | source_match_set.insert(x: source_info); |
948 | if (DisplayFunctionSource(sc, source_info, result)) |
949 | displayed_something = true; |
950 | } |
951 | } |
952 | if (displayed_something) |
953 | result.SetStatus(eReturnStatusSuccessFinishResult); |
954 | else |
955 | result.SetStatus(eReturnStatusFailed); |
956 | return; |
957 | } else if (m_options.address != LLDB_INVALID_ADDRESS) { |
958 | Address so_addr; |
959 | StreamString error_strm; |
960 | SymbolContextList sc_list; |
961 | |
962 | if (!target.HasLoadedSections()) { |
963 | // The target isn't loaded yet, we need to lookup the file address in |
964 | // all modules |
965 | const ModuleList &module_list = target.GetImages(); |
966 | const size_t num_modules = module_list.GetSize(); |
967 | for (size_t i = 0; i < num_modules; ++i) { |
968 | ModuleSP module_sp(module_list.GetModuleAtIndex(idx: i)); |
969 | if (module_sp && |
970 | module_sp->ResolveFileAddress(vm_addr: m_options.address, so_addr)) { |
971 | SymbolContext sc; |
972 | sc.Clear(clear_target: true); |
973 | if (module_sp->ResolveSymbolContextForAddress( |
974 | so_addr, resolve_scope: eSymbolContextEverything, sc) & |
975 | eSymbolContextLineEntry) |
976 | sc_list.Append(sc); |
977 | } |
978 | } |
979 | |
980 | if (sc_list.GetSize() == 0) { |
981 | result.AppendErrorWithFormat( |
982 | format: "no modules have source information for file address 0x%"PRIx64 |
983 | ".\n", |
984 | m_options.address); |
985 | return; |
986 | } |
987 | } else { |
988 | // The target has some things loaded, resolve this address to a compile |
989 | // unit + file + line and display |
990 | if (target.ResolveLoadAddress(load_addr: m_options.address, so_addr)) { |
991 | ModuleSP module_sp(so_addr.GetModule()); |
992 | if (module_sp) { |
993 | SymbolContext sc; |
994 | sc.Clear(clear_target: true); |
995 | if (module_sp->ResolveSymbolContextForAddress( |
996 | so_addr, resolve_scope: eSymbolContextEverything, sc) & |
997 | eSymbolContextLineEntry) { |
998 | sc_list.Append(sc); |
999 | } else { |
1000 | so_addr.Dump(s: &error_strm, exe_scope: nullptr, |
1001 | style: Address::DumpStyleModuleWithFileAddress); |
1002 | result.AppendErrorWithFormat(format: "address resolves to %s, but there " |
1003 | "is no line table information " |
1004 | "available for this address.\n", |
1005 | error_strm.GetData()); |
1006 | return; |
1007 | } |
1008 | } |
1009 | } |
1010 | |
1011 | if (sc_list.GetSize() == 0) { |
1012 | result.AppendErrorWithFormat( |
1013 | format: "no modules contain load address 0x%"PRIx64 ".\n", |
1014 | m_options.address); |
1015 | return; |
1016 | } |
1017 | } |
1018 | for (const SymbolContext &sc : sc_list) { |
1019 | if (sc.comp_unit) { |
1020 | if (m_options.show_bp_locs) { |
1021 | m_breakpoint_locations.Clear(); |
1022 | const bool show_inlines = true; |
1023 | m_breakpoint_locations.Reset(file_spec: sc.comp_unit->GetPrimaryFile(), line: 0, |
1024 | check_inlines: show_inlines); |
1025 | SearchFilterForUnconstrainedSearches target_search_filter( |
1026 | target.shared_from_this()); |
1027 | target_search_filter.Search(searcher&: m_breakpoint_locations); |
1028 | } |
1029 | |
1030 | bool show_fullpaths = true; |
1031 | bool show_module = true; |
1032 | bool show_inlined_frames = true; |
1033 | const bool show_function_arguments = true; |
1034 | const bool show_function_name = true; |
1035 | sc.DumpStopContext(s: &result.GetOutputStream(), |
1036 | exe_scope: m_exe_ctx.GetBestExecutionContextScope(), |
1037 | so_addr: sc.line_entry.range.GetBaseAddress(), |
1038 | show_fullpaths, show_module, show_inlined_frames, |
1039 | show_function_arguments, show_function_name); |
1040 | result.GetOutputStream().EOL(); |
1041 | |
1042 | if (m_options.num_lines == 0) |
1043 | m_options.num_lines = 10; |
1044 | |
1045 | size_t lines_to_back_up = |
1046 | m_options.num_lines >= 10 ? 5 : m_options.num_lines / 2; |
1047 | |
1048 | const uint32_t column = |
1049 | (GetDebugger().GetStopShowColumn() != eStopShowColumnNone) |
1050 | ? sc.line_entry.column |
1051 | : 0; |
1052 | target.GetSourceManager().DisplaySourceLinesWithLineNumbers( |
1053 | support_file_sp: sc.comp_unit->GetPrimarySupportFile(), |
1054 | line: sc.line_entry.line, column, context_before: lines_to_back_up, |
1055 | context_after: m_options.num_lines - lines_to_back_up, current_line_cstr: "->", |
1056 | s: &result.GetOutputStream(), bp_locs: GetBreakpointLocations()); |
1057 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1058 | } |
1059 | } |
1060 | } else if (m_options.file_name.empty()) { |
1061 | // Last valid source manager context, or the current frame if no valid |
1062 | // last context in source manager. One little trick here, if you type the |
1063 | // exact same list command twice in a row, it is more likely because you |
1064 | // typed it once, then typed it again |
1065 | if (m_options.start_line == 0) { |
1066 | if (target.GetSourceManager().DisplayMoreWithLineNumbers( |
1067 | s: &result.GetOutputStream(), count: m_options.num_lines, |
1068 | reverse: m_options.reverse, bp_locs: GetBreakpointLocations())) { |
1069 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1070 | } else { |
1071 | if (target.GetSourceManager().AtLastLine(reverse: m_options.reverse)) { |
1072 | result.AppendNoteWithFormatv( |
1073 | format: "Reached {0} of the file, no more to page", |
1074 | args: m_options.reverse ? "beginning": "end"); |
1075 | } else { |
1076 | result.AppendNote(in_string: "No source available"); |
1077 | } |
1078 | } |
1079 | |
1080 | } else { |
1081 | if (m_options.num_lines == 0) |
1082 | m_options.num_lines = 10; |
1083 | |
1084 | if (m_options.show_bp_locs) { |
1085 | SourceManager::FileSP last_file_sp( |
1086 | target.GetSourceManager().GetLastFile()); |
1087 | if (last_file_sp) { |
1088 | const bool show_inlines = true; |
1089 | m_breakpoint_locations.Reset( |
1090 | file_spec: last_file_sp->GetSupportFile()->GetSpecOnly(), line: 0, check_inlines: show_inlines); |
1091 | SearchFilterForUnconstrainedSearches target_search_filter( |
1092 | target.shared_from_this()); |
1093 | target_search_filter.Search(searcher&: m_breakpoint_locations); |
1094 | } |
1095 | } else |
1096 | m_breakpoint_locations.Clear(); |
1097 | |
1098 | const uint32_t column = 0; |
1099 | if (target.GetSourceManager() |
1100 | .DisplaySourceLinesWithLineNumbersUsingLastFile( |
1101 | start_line: m_options.start_line, // Line to display |
1102 | count: m_options.num_lines, // Lines after line to |
1103 | UINT32_MAX, // Don't mark "line" |
1104 | column, |
1105 | current_line_cstr: "", // Don't mark "line" |
1106 | s: &result.GetOutputStream(), bp_locs: GetBreakpointLocations())) { |
1107 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1108 | } |
1109 | } |
1110 | } else { |
1111 | // const char *filename = m_options.file_name.c_str(); |
1112 | FileSpec file_spec(m_options.file_name); |
1113 | bool check_inlines = false; |
1114 | const InlineStrategy inline_strategy = target.GetInlineStrategy(); |
1115 | if (inline_strategy == eInlineBreakpointsAlways || |
1116 | (inline_strategy == eInlineBreakpointsHeaders && |
1117 | !file_spec.IsSourceImplementationFile())) |
1118 | check_inlines = true; |
1119 | |
1120 | SymbolContextList sc_list; |
1121 | size_t num_matches = 0; |
1122 | |
1123 | if (!m_options.modules.empty()) { |
1124 | ModuleList matching_modules; |
1125 | for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) { |
1126 | FileSpec module_file_spec(m_options.modules[i]); |
1127 | if (module_file_spec) { |
1128 | ModuleSpec module_spec(module_file_spec); |
1129 | matching_modules.Clear(); |
1130 | target.GetImages().FindModules(module_spec, matching_module_list&: matching_modules); |
1131 | num_matches += matching_modules.ResolveSymbolContextsForFileSpec( |
1132 | file_spec, line: 1, check_inlines, |
1133 | resolve_scope: SymbolContextItem(eSymbolContextModule | |
1134 | eSymbolContextCompUnit | |
1135 | eSymbolContextLineEntry), |
1136 | sc_list); |
1137 | } |
1138 | } |
1139 | } else { |
1140 | num_matches = target.GetImages().ResolveSymbolContextsForFileSpec( |
1141 | file_spec, line: 1, check_inlines, |
1142 | resolve_scope: eSymbolContextModule | eSymbolContextCompUnit | |
1143 | eSymbolContextLineEntry, |
1144 | sc_list); |
1145 | } |
1146 | |
1147 | if (num_matches == 0) { |
1148 | result.AppendErrorWithFormat(format: "Could not find source file \"%s\".\n", |
1149 | m_options.file_name.c_str()); |
1150 | return; |
1151 | } |
1152 | |
1153 | if (num_matches > 1) { |
1154 | bool got_multiple = false; |
1155 | CompileUnit *test_cu = nullptr; |
1156 | |
1157 | for (const SymbolContext &sc : sc_list) { |
1158 | if (sc.comp_unit) { |
1159 | if (test_cu) { |
1160 | if (test_cu != sc.comp_unit) |
1161 | got_multiple = true; |
1162 | break; |
1163 | } else |
1164 | test_cu = sc.comp_unit; |
1165 | } |
1166 | } |
1167 | if (got_multiple) { |
1168 | result.AppendErrorWithFormat( |
1169 | format: "Multiple source files found matching: \"%s.\"\n", |
1170 | m_options.file_name.c_str()); |
1171 | return; |
1172 | } |
1173 | } |
1174 | |
1175 | SymbolContext sc; |
1176 | if (sc_list.GetContextAtIndex(idx: 0, sc)) { |
1177 | if (sc.comp_unit) { |
1178 | if (m_options.show_bp_locs) { |
1179 | const bool show_inlines = true; |
1180 | m_breakpoint_locations.Reset(file_spec: sc.comp_unit->GetPrimaryFile(), line: 0, |
1181 | check_inlines: show_inlines); |
1182 | SearchFilterForUnconstrainedSearches target_search_filter( |
1183 | target.shared_from_this()); |
1184 | target_search_filter.Search(searcher&: m_breakpoint_locations); |
1185 | } else |
1186 | m_breakpoint_locations.Clear(); |
1187 | |
1188 | if (m_options.num_lines == 0) |
1189 | m_options.num_lines = 10; |
1190 | const uint32_t column = 0; |
1191 | |
1192 | // Headers aren't always in the DWARF but if they have |
1193 | // executable code (eg., inlined-functions) then the callsite's |
1194 | // file(s) will be found and assigned to |
1195 | // sc.comp_unit->GetPrimarySupportFile, which is NOT what we want to |
1196 | // print. Instead, we want to print the one from the line entry. |
1197 | lldb::SupportFileSP found_file_sp = sc.line_entry.file_sp; |
1198 | |
1199 | target.GetSourceManager().DisplaySourceLinesWithLineNumbers( |
1200 | support_file_sp: found_file_sp, line: m_options.start_line, column, context_before: 0, |
1201 | context_after: m_options.num_lines, current_line_cstr: "", s: &result.GetOutputStream(), |
1202 | bp_locs: GetBreakpointLocations()); |
1203 | |
1204 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1205 | } else { |
1206 | result.AppendErrorWithFormat(format: "No comp unit found for: \"%s.\"\n", |
1207 | m_options.file_name.c_str()); |
1208 | } |
1209 | } |
1210 | } |
1211 | } |
1212 | |
1213 | const SymbolContextList *GetBreakpointLocations() { |
1214 | if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0) |
1215 | return &m_breakpoint_locations.GetFileLineMatches(); |
1216 | return nullptr; |
1217 | } |
1218 | |
1219 | CommandOptions m_options; |
1220 | FileLineResolver m_breakpoint_locations; |
1221 | std::string m_reverse_name; |
1222 | }; |
1223 | |
1224 | class CommandObjectSourceCacheDump : public CommandObjectParsed { |
1225 | public: |
1226 | CommandObjectSourceCacheDump(CommandInterpreter &interpreter) |
1227 | : CommandObjectParsed(interpreter, "source cache dump", |
1228 | "Dump the state of the source code cache. Intended " |
1229 | "to be used for debugging LLDB itself.", |
1230 | nullptr) {} |
1231 | |
1232 | ~CommandObjectSourceCacheDump() override = default; |
1233 | |
1234 | protected: |
1235 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1236 | // Dump the debugger source cache. |
1237 | result.GetOutputStream() << "Debugger Source File Cache\n"; |
1238 | SourceManager::SourceFileCache &cache = GetDebugger().GetSourceFileCache(); |
1239 | cache.Dump(stream&: result.GetOutputStream()); |
1240 | |
1241 | // Dump the process source cache. |
1242 | if (ProcessSP process_sp = m_exe_ctx.GetProcessSP()) { |
1243 | result.GetOutputStream() << "\nProcess Source File Cache\n"; |
1244 | SourceManager::SourceFileCache &cache = process_sp->GetSourceFileCache(); |
1245 | cache.Dump(stream&: result.GetOutputStream()); |
1246 | } |
1247 | |
1248 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1249 | } |
1250 | }; |
1251 | |
1252 | class CommandObjectSourceCacheClear : public CommandObjectParsed { |
1253 | public: |
1254 | CommandObjectSourceCacheClear(CommandInterpreter &interpreter) |
1255 | : CommandObjectParsed(interpreter, "source cache clear", |
1256 | "Clear the source code cache.\n", nullptr) {} |
1257 | |
1258 | ~CommandObjectSourceCacheClear() override = default; |
1259 | |
1260 | protected: |
1261 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1262 | // Clear the debugger cache. |
1263 | SourceManager::SourceFileCache &cache = GetDebugger().GetSourceFileCache(); |
1264 | cache.Clear(); |
1265 | |
1266 | // Clear the process cache. |
1267 | if (ProcessSP process_sp = m_exe_ctx.GetProcessSP()) |
1268 | process_sp->GetSourceFileCache().Clear(); |
1269 | |
1270 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1271 | } |
1272 | }; |
1273 | |
1274 | class CommandObjectSourceCache : public CommandObjectMultiword { |
1275 | public: |
1276 | CommandObjectSourceCache(CommandInterpreter &interpreter) |
1277 | : CommandObjectMultiword(interpreter, "source cache", |
1278 | "Commands for managing the source code cache.", |
1279 | "source cache <sub-command>") { |
1280 | LoadSubCommand( |
1281 | cmd_name: "dump", command_obj: CommandObjectSP(new CommandObjectSourceCacheDump(interpreter))); |
1282 | LoadSubCommand(cmd_name: "clear", command_obj: CommandObjectSP(new CommandObjectSourceCacheClear( |
1283 | interpreter))); |
1284 | } |
1285 | |
1286 | ~CommandObjectSourceCache() override = default; |
1287 | |
1288 | private: |
1289 | CommandObjectSourceCache(const CommandObjectSourceCache &) = delete; |
1290 | const CommandObjectSourceCache & |
1291 | operator=(const CommandObjectSourceCache &) = delete; |
1292 | }; |
1293 | |
1294 | #pragma mark CommandObjectMultiwordSource |
1295 | // CommandObjectMultiwordSource |
1296 | |
1297 | CommandObjectMultiwordSource::CommandObjectMultiwordSource( |
1298 | CommandInterpreter &interpreter) |
1299 | : CommandObjectMultiword(interpreter, "source", |
1300 | "Commands for examining " |
1301 | "source code described by " |
1302 | "debug information for the " |
1303 | "current target process.", |
1304 | "source <subcommand> [<subcommand-options>]") { |
1305 | LoadSubCommand(cmd_name: "info", |
1306 | command_obj: CommandObjectSP(new CommandObjectSourceInfo(interpreter))); |
1307 | LoadSubCommand(cmd_name: "list", |
1308 | command_obj: CommandObjectSP(new CommandObjectSourceList(interpreter))); |
1309 | LoadSubCommand(cmd_name: "cache", |
1310 | command_obj: CommandObjectSP(new CommandObjectSourceCache(interpreter))); |
1311 | } |
1312 | |
1313 | CommandObjectMultiwordSource::~CommandObjectMultiwordSource() = default; |
1314 |
Definitions
- CommandObjectSourceInfo
- CommandOptions
- CommandOptions
- ~CommandOptions
- SetOptionValue
- OptionParsingStarting
- GetDefinitions
- CommandObjectSourceInfo
- ~CommandObjectSourceInfo
- GetOptions
- DumpLinesInSymbolContexts
- DumpFileLinesInCompUnit
- DumpFileLinesInModule
- GetSymbolContextsForAddress
- DumpLinesInFunctions
- DumpLinesForAddress
- DumpLinesForFile
- DumpLinesForFrame
- DoExecute
- CommandObjectSourceList
- CommandOptions
- CommandOptions
- ~CommandOptions
- SetOptionValue
- OptionParsingStarting
- GetDefinitions
- CommandObjectSourceList
- ~CommandObjectSourceList
- GetOptions
- GetRepeatCommand
- SourceInfo
- SourceInfo
- SourceInfo
- IsValid
- operator==
- operator!=
- operator<
- DisplayFunctionSource
- FindMatchingFunctions
- FindMatchingFunctionSymbols
- DoExecute
- GetBreakpointLocations
- CommandObjectSourceCacheDump
- CommandObjectSourceCacheDump
- ~CommandObjectSourceCacheDump
- DoExecute
- CommandObjectSourceCacheClear
- CommandObjectSourceCacheClear
- ~CommandObjectSourceCacheClear
- DoExecute
- CommandObjectSourceCache
- CommandObjectSourceCache
- ~CommandObjectSourceCache
- CommandObjectSourceCache
- operator=
- CommandObjectMultiwordSource
Learn to use CMake with our Intro Training
Find out more