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