xref: /llvm-project/lldb/source/Commands/CommandObjectSource.cpp (revision 3300d778ab7dfbfabcf4d89e01bc98a420488f56)
1 //===-- CommandObjectSource.cpp ---------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "CommandObjectSource.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/Interpreter/Args.h"
17 #include "lldb/Core/Debugger.h"
18 #include "lldb/Core/FileLineResolver.h"
19 #include "lldb/Core/SourceManager.h"
20 #include "lldb/Interpreter/CommandInterpreter.h"
21 #include "lldb/Interpreter/CommandReturnObject.h"
22 #include "lldb/Host/FileSpec.h"
23 #include "lldb/Target/Process.h"
24 #include "lldb/Target/TargetList.h"
25 #include "lldb/Interpreter/CommandCompletions.h"
26 #include "lldb/Interpreter/Options.h"
27 
28 using namespace lldb;
29 using namespace lldb_private;
30 
31 //-------------------------------------------------------------------------
32 // CommandObjectSourceInfo
33 //-------------------------------------------------------------------------
34 
35 class CommandObjectSourceInfo : public CommandObject
36 {
37 
38     class CommandOptions : public Options
39     {
40     public:
41         CommandOptions (CommandInterpreter &interpreter) :
42             Options(interpreter)
43         {
44         }
45 
46         ~CommandOptions ()
47         {
48         }
49 
50         Error
51         SetOptionValue (uint32_t option_idx, const char *option_arg)
52         {
53             Error error;
54             const char short_option = g_option_table[option_idx].short_option;
55             switch (short_option)
56             {
57             case 'l':
58                 start_line = Args::StringToUInt32 (option_arg, 0);
59                 if (start_line == 0)
60                     error.SetErrorStringWithFormat("Invalid line number: '%s'.\n", option_arg);
61                 break;
62 
63              case 'f':
64                 file_name = option_arg;
65                 break;
66 
67            default:
68                 error.SetErrorStringWithFormat("Unrecognized short option '%c'.\n", short_option);
69                 break;
70             }
71 
72             return error;
73         }
74 
75         void
76         OptionParsingStarting ()
77         {
78             file_spec.Clear();
79             file_name.clear();
80             start_line = 0;
81         }
82 
83         const OptionDefinition*
84         GetDefinitions ()
85         {
86             return g_option_table;
87         }
88         static OptionDefinition g_option_table[];
89 
90         // Instance variables to hold the values for command options.
91         FileSpec file_spec;
92         std::string file_name;
93         uint32_t start_line;
94 
95     };
96 
97 public:
98     CommandObjectSourceInfo(CommandInterpreter &interpreter) :
99         CommandObject (interpreter,
100                        "source info",
101                        "Display information about the source lines from the current executable's debug info.",
102                        "source info [<cmd-options>]"),
103         m_options (interpreter)
104     {
105     }
106 
107     ~CommandObjectSourceInfo ()
108     {
109     }
110 
111 
112     Options *
113     GetOptions ()
114     {
115         return &m_options;
116     }
117 
118 
119     bool
120     Execute
121     (
122         Args& args,
123         CommandReturnObject &result
124     )
125     {
126         result.AppendError ("Not yet implemented");
127         result.SetStatus (eReturnStatusFailed);
128         return false;
129     }
130 protected:
131     CommandOptions m_options;
132 };
133 
134 OptionDefinition
135 CommandObjectSourceInfo::CommandOptions::g_option_table[] =
136 {
137 { LLDB_OPT_SET_1, false, "line",       'l', required_argument, NULL, 0, eArgTypeLineNum,    "The line number at which to start the display source."},
138 { LLDB_OPT_SET_1, false, "file",       'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename,    "The file from which to display source."},
139 { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
140 };
141 
142 #pragma mark CommandObjectSourceList
143 //-------------------------------------------------------------------------
144 // CommandObjectSourceList
145 //-------------------------------------------------------------------------
146 
147 class CommandObjectSourceList : public CommandObject
148 {
149 
150     class CommandOptions : public Options
151     {
152     public:
153         CommandOptions (CommandInterpreter &interpreter) :
154             Options(interpreter)
155         {
156         }
157 
158         ~CommandOptions ()
159         {
160         }
161 
162         Error
163         SetOptionValue (uint32_t option_idx, const char *option_arg)
164         {
165             Error error;
166             const char short_option = g_option_table[option_idx].short_option;
167             switch (short_option)
168             {
169             case 'l':
170                 start_line = Args::StringToUInt32 (option_arg, 0);
171                 if (start_line == 0)
172                     error.SetErrorStringWithFormat("Invalid line number: '%s'.\n", option_arg);
173                 break;
174 
175             case 'c':
176                 num_lines = Args::StringToUInt32 (option_arg, 0);
177                 if (num_lines == 0)
178                     error.SetErrorStringWithFormat("Invalid line count: '%s'.\n", option_arg);
179                 break;
180 
181             case 'f':
182                 file_name = option_arg;
183                 break;
184 
185             case 'n':
186                 symbol_name = option_arg;
187                 break;
188 
189             case 's':
190                 modules.push_back (std::string (option_arg));
191                 break;
192 
193             case 'b':
194                 show_bp_locs = true;
195                 break;
196            default:
197                 error.SetErrorStringWithFormat("Unrecognized short option '%c'.\n", short_option);
198                 break;
199             }
200 
201             return error;
202         }
203 
204         void
205         OptionParsingStarting ()
206         {
207             file_spec.Clear();
208             file_name.clear();
209             symbol_name.clear();
210             start_line = 0;
211             num_lines = 10;
212             show_bp_locs = false;
213             modules.clear();
214         }
215 
216         const OptionDefinition*
217         GetDefinitions ()
218         {
219             return g_option_table;
220         }
221         static OptionDefinition g_option_table[];
222 
223         // Instance variables to hold the values for command options.
224         FileSpec file_spec;
225         std::string file_name;
226         std::string symbol_name;
227         uint32_t start_line;
228         uint32_t num_lines;
229         STLStringArray modules;
230         bool show_bp_locs;
231     };
232 
233 public:
234     CommandObjectSourceList(CommandInterpreter &interpreter) :
235         CommandObject (interpreter,
236                        "source list",
237                        "Display source code (as specified) based on the current executable's debug info.",
238                         NULL),
239         m_options (interpreter)
240     {
241         CommandArgumentEntry arg;
242         CommandArgumentData file_arg;
243 
244         // Define the first (and only) variant of this arg.
245         file_arg.arg_type = eArgTypeFilename;
246         file_arg.arg_repetition = eArgRepeatOptional;
247 
248         // There is only one variant this argument could be; put it into the argument entry.
249         arg.push_back (file_arg);
250 
251         // Push the data for the first argument into the m_arguments vector.
252         m_arguments.push_back (arg);
253     }
254 
255     ~CommandObjectSourceList ()
256     {
257     }
258 
259 
260     Options *
261     GetOptions ()
262     {
263         return &m_options;
264     }
265 
266 
267     bool
268     Execute
269     (
270         Args& args,
271         CommandReturnObject &result
272     )
273     {
274         const int argc = args.GetArgumentCount();
275 
276         if (argc != 0)
277         {
278             result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n", GetCommandName());
279             result.SetStatus (eReturnStatusFailed);
280         }
281 
282         ExecutionContext exe_ctx(m_interpreter.GetExecutionContext());
283 
284         if (!m_options.symbol_name.empty())
285         {
286             // Displaying the source for a symbol:
287             Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
288             if (target == NULL)
289             {
290                 result.AppendError ("invalid target, set executable file using 'file' command");
291                 result.SetStatus (eReturnStatusFailed);
292                 return false;
293             }
294 
295             SymbolContextList sc_list;
296             ConstString name(m_options.symbol_name.c_str());
297             bool include_symbols = false;
298             bool append = true;
299             size_t num_matches = 0;
300 
301             if (m_options.modules.size() > 0)
302             {
303                 ModuleList matching_modules;
304                 for (unsigned i = 0, e = m_options.modules.size(); i != e; i++)
305                 {
306                     FileSpec module_spec(m_options.modules[i].c_str(), false);
307                     if (module_spec)
308                     {
309                         matching_modules.Clear();
310                         target->GetImages().FindModules (&module_spec, NULL, NULL, NULL, matching_modules);
311                         num_matches += matching_modules.FindFunctions (name, eFunctionNameTypeBase, include_symbols, append, sc_list);
312                     }
313                 }
314             }
315             else
316             {
317                 num_matches = target->GetImages().FindFunctions (name, eFunctionNameTypeBase, include_symbols, append, sc_list);
318             }
319 
320             SymbolContext sc;
321 
322             if (num_matches == 0)
323             {
324                 result.AppendErrorWithFormat("Could not find function named: \"%s\".\n", m_options.symbol_name.c_str());
325                 result.SetStatus (eReturnStatusFailed);
326                 return false;
327             }
328 
329             sc_list.GetContextAtIndex (0, sc);
330             FileSpec start_file;
331             uint32_t start_line;
332             uint32_t end_line;
333             FileSpec end_file;
334             if (sc.function != NULL)
335             {
336                 sc.function->GetStartLineSourceInfo (start_file, start_line);
337                 if (start_line == 0)
338                 {
339                     result.AppendErrorWithFormat("Could not find line information for start of function: \"%s\".\n", m_options.symbol_name.c_str());
340                     result.SetStatus (eReturnStatusFailed);
341                     return false;
342                 }
343                 sc.function->GetEndLineSourceInfo (end_file, end_line);
344             }
345             else
346             {
347                 result.AppendErrorWithFormat("Could not find function info for: \"%s\".\n", m_options.symbol_name.c_str());
348                 result.SetStatus (eReturnStatusFailed);
349                 return false;
350             }
351 
352             if (num_matches > 1)
353             {
354                 // This could either be because there are multiple functions of this name, in which case
355                 // we'll have to specify this further...  Or it could be because there are multiple inlined instances
356                 // of one function.  So run through the matches and if they all have the same file & line then we can just
357                 // list one.
358 
359                 bool found_multiple = false;
360 
361                 for (size_t i = 1; i < num_matches; i++)
362                 {
363                     SymbolContext scratch_sc;
364                     sc_list.GetContextAtIndex (i, scratch_sc);
365                     if (scratch_sc.function != NULL)
366                     {
367                         FileSpec scratch_file;
368                         uint32_t scratch_line;
369                         scratch_sc.function->GetStartLineSourceInfo (scratch_file, scratch_line);
370                         if (scratch_file != start_file
371                             || scratch_line != start_line)
372                         {
373                             found_multiple = true;
374                             break;
375                         }
376                     }
377                 }
378                 if (found_multiple)
379                 {
380                     StreamString s;
381                     for (size_t i = 0; i < num_matches; i++)
382                     {
383                         SymbolContext scratch_sc;
384                         sc_list.GetContextAtIndex (i, scratch_sc);
385                         if (scratch_sc.function != NULL)
386                         {
387                             s.Printf("\n%d: ", i);
388                             scratch_sc.function->Dump (&s, true);
389                         }
390                     }
391                     result.AppendErrorWithFormat("Multiple functions found matching: %s: \n%s\n",
392                                                  m_options.symbol_name.c_str(),
393                                                  s.GetData());
394                     result.SetStatus (eReturnStatusFailed);
395                     return false;
396                 }
397             }
398 
399 
400             // This is a little hacky, but the first line table entry for a function points to the "{" that
401             // starts the function block.  It would be nice to actually get the function
402             // declaration in there too.  So back up a bit, but not further than what you're going to display.
403             size_t lines_to_back_up = m_options.num_lines >= 10 ? 5 : m_options.num_lines/2;
404             uint32_t line_no;
405             if (start_line <= lines_to_back_up)
406                 line_no = 1;
407             else
408                 line_no = start_line - lines_to_back_up;
409 
410             // For fun, if the function is shorter than the number of lines we're supposed to display,
411             // only display the function...
412             if (end_line != 0)
413             {
414                 if (m_options.num_lines > end_line - line_no)
415                     m_options.num_lines = end_line - line_no;
416             }
417 
418             char path_buf[PATH_MAX];
419             start_file.GetPath(path_buf, sizeof(path_buf));
420 
421             if (m_options.show_bp_locs && exe_ctx.target)
422             {
423                 const bool show_inlines = true;
424                 m_breakpoint_locations.Reset (start_file, 0, show_inlines);
425                 SearchFilter target_search_filter (exe_ctx.target->GetSP());
426                 target_search_filter.Search (m_breakpoint_locations);
427             }
428             else
429                 m_breakpoint_locations.Clear();
430 
431             result.AppendMessageWithFormat("File: %s.\n", path_buf);
432             m_interpreter.GetDebugger().GetSourceManager().DisplaySourceLinesWithLineNumbers (start_file,
433                                                                                               line_no,
434                                                                                               0,
435                                                                                               m_options.num_lines,
436                                                                                               "",
437                                                                                               &result.GetOutputStream(),
438                                                                                               GetBreakpointLocations ());
439 
440             result.SetStatus (eReturnStatusSuccessFinishResult);
441             return true;
442 
443         }
444         else if (m_options.file_name.empty())
445         {
446             // Last valid source manager context, or the current frame if no
447             // valid last context in source manager.
448             // One little trick here, if you type the exact same list command twice in a row, it is
449             // more likely because you typed it once, then typed it again
450             if (m_options.start_line == 0)
451             {
452                 if (m_interpreter.GetDebugger().GetSourceManager().DisplayMoreWithLineNumbers (&result.GetOutputStream(),
453                                                                                                GetBreakpointLocations ()))
454                 {
455                     result.SetStatus (eReturnStatusSuccessFinishResult);
456                 }
457             }
458             else
459             {
460                 if (m_options.show_bp_locs && exe_ctx.target)
461                 {
462                     SourceManager::FileSP last_file_sp (m_interpreter.GetDebugger().GetSourceManager().GetLastFile ());
463                     if (last_file_sp)
464                     {
465                         const bool show_inlines = true;
466                         m_breakpoint_locations.Reset (last_file_sp->GetFileSpec(), 0, show_inlines);
467                         SearchFilter target_search_filter (exe_ctx.target->GetSP());
468                         target_search_filter.Search (m_breakpoint_locations);
469                     }
470                 }
471                 else
472                     m_breakpoint_locations.Clear();
473 
474                 if (m_interpreter.GetDebugger().GetSourceManager().DisplaySourceLinesWithLineNumbersUsingLastFile(
475                             m_options.start_line,   // Line to display
476                             0,                      // Lines before line to display
477                             m_options.num_lines,    // Lines after line to display
478                             "",                     // Don't mark "line"
479                             &result.GetOutputStream(),
480                             GetBreakpointLocations ()))
481                 {
482                     result.SetStatus (eReturnStatusSuccessFinishResult);
483                 }
484 
485             }
486         }
487         else
488         {
489             const char *filename = m_options.file_name.c_str();
490             Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
491             if (target == NULL)
492             {
493                 result.AppendError ("invalid target, set executable file using 'file' command");
494                 result.SetStatus (eReturnStatusFailed);
495                 return false;
496             }
497 
498 
499             bool check_inlines = false;
500             SymbolContextList sc_list;
501             size_t num_matches = 0;
502 
503             if (m_options.modules.size() > 0)
504             {
505                 ModuleList matching_modules;
506                 for (unsigned i = 0, e = m_options.modules.size(); i != e; i++)
507                 {
508                     FileSpec module_spec(m_options.modules[i].c_str(), false);
509                     if (module_spec)
510                     {
511                         matching_modules.Clear();
512                         target->GetImages().FindModules (&module_spec, NULL, NULL, NULL, matching_modules);
513                         num_matches += matching_modules.ResolveSymbolContextForFilePath (filename,
514                                                                                          0,
515                                                                                          check_inlines,
516                                                                                          eSymbolContextModule | eSymbolContextCompUnit,
517                                                                                          sc_list);
518                     }
519                 }
520             }
521             else
522             {
523                 num_matches = target->GetImages().ResolveSymbolContextForFilePath (filename,
524                                                                                    0,
525                                                                                    check_inlines,
526                                                                                    eSymbolContextModule | eSymbolContextCompUnit,
527                                                                                    sc_list);
528             }
529 
530             if (num_matches == 0)
531             {
532                 result.AppendErrorWithFormat("Could not find source file \"%s\".\n",
533                                              m_options.file_name.c_str());
534                 result.SetStatus (eReturnStatusFailed);
535                 return false;
536             }
537 
538             if (num_matches > 1)
539             {
540                 SymbolContext sc;
541                 bool got_multiple = false;
542                 FileSpec *test_cu_spec = NULL;
543 
544                 for (unsigned i = 0; i < num_matches; i++)
545                 {
546                     sc_list.GetContextAtIndex(i, sc);
547                     if (sc.comp_unit)
548                     {
549                         if (test_cu_spec)
550                         {
551                             if (test_cu_spec != static_cast<FileSpec *> (sc.comp_unit))
552                                 got_multiple = true;
553                                 break;
554                         }
555                         else
556                             test_cu_spec = sc.comp_unit;
557                     }
558                 }
559                 if (got_multiple)
560                 {
561                     result.AppendErrorWithFormat("Multiple source files found matching: \"%s.\"\n",
562                                                  m_options.file_name.c_str());
563                     result.SetStatus (eReturnStatusFailed);
564                     return false;
565                 }
566             }
567 
568             SymbolContext sc;
569             if (sc_list.GetContextAtIndex(0, sc))
570             {
571                 if (sc.comp_unit)
572                 {
573                     if (m_options.show_bp_locs && exe_ctx.target)
574                     {
575                         const bool show_inlines = true;
576                         m_breakpoint_locations.Reset (*sc.comp_unit, 0, show_inlines);
577                         SearchFilter target_search_filter (exe_ctx.target->GetSP());
578                         target_search_filter.Search (m_breakpoint_locations);
579                     }
580                     else
581                         m_breakpoint_locations.Clear();
582 
583                     m_interpreter.GetDebugger().GetSourceManager().DisplaySourceLinesWithLineNumbers (sc.comp_unit,
584                                                                                                       m_options.start_line,
585                                                                                                       0,
586                                                                                                       m_options.num_lines,
587                                                                                                       "",
588                                                                                                       &result.GetOutputStream(),
589                                                                                                       GetBreakpointLocations ());
590 
591                     result.SetStatus (eReturnStatusSuccessFinishResult);
592                 }
593                 else
594                 {
595                     result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n",
596                                                  m_options.file_name.c_str());
597                     result.SetStatus (eReturnStatusFailed);
598                     return false;
599                 }
600             }
601         }
602         return result.Succeeded();
603     }
604 
605     virtual const char *GetRepeatCommand (Args &current_command_args, uint32_t index)
606     {
607         return m_cmd_name.c_str();
608     }
609 
610 protected:
611     const SymbolContextList *
612     GetBreakpointLocations ()
613     {
614         if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0)
615             return &m_breakpoint_locations.GetFileLineMatches();
616         return NULL;
617     }
618     CommandOptions m_options;
619     FileLineResolver m_breakpoint_locations;
620 
621 };
622 
623 OptionDefinition
624 CommandObjectSourceList::CommandOptions::g_option_table[] =
625 {
626 { LLDB_OPT_SET_ALL, false, "count",    'c', required_argument, NULL, 0, eArgTypeCount,   "The number of source lines to display."},
627 { LLDB_OPT_SET_ALL, false, "shlib",    's', required_argument, NULL, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Look up the source file in the given shared library."},
628 { LLDB_OPT_SET_ALL, false, "show-breakpoints", 'b', no_argument, NULL, 0, eArgTypeNone, "Show the line table locations from the debug information that indicate valid places to set source level breakpoints."},
629 { LLDB_OPT_SET_1, false, "file",       'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename,    "The file from which to display source."},
630 { LLDB_OPT_SET_1, false, "line",       'l', required_argument, NULL, 0, eArgTypeLineNum,    "The line number at which to start the display source."},
631 { LLDB_OPT_SET_2, false, "name",       'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, eArgTypeSymbol,    "The name of a function whose source to display."},
632 { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
633 };
634 
635 #pragma mark CommandObjectMultiwordSource
636 
637 //-------------------------------------------------------------------------
638 // CommandObjectMultiwordSource
639 //-------------------------------------------------------------------------
640 
641 CommandObjectMultiwordSource::CommandObjectMultiwordSource (CommandInterpreter &interpreter) :
642     CommandObjectMultiword (interpreter,
643                             "source",
644                             "A set of commands for accessing source file information",
645                             "source <subcommand> [<subcommand-options>]")
646 {
647     LoadSubCommand ("info",   CommandObjectSP (new CommandObjectSourceInfo (interpreter)));
648     LoadSubCommand ("list",   CommandObjectSP (new CommandObjectSourceList (interpreter)));
649 }
650 
651 CommandObjectMultiwordSource::~CommandObjectMultiwordSource ()
652 {
653 }
654 
655