xref: /llvm-project/lldb/source/Commands/CommandObjectSource.cpp (revision 405fe67f1424284173459575b1081f42935c50fd)
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/Core/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 append = true;
293             size_t num_matches = 0;
294 
295             if (m_options.m_modules.size() > 0)
296             {
297                 ModuleList matching_modules;
298                 for (unsigned i = 0, e = m_options.m_modules.size(); i != e; i++)
299                 {
300                     FileSpec module_spec(m_options.m_modules[i].c_str());
301                     if (module_spec)
302                     {
303                         matching_modules.Clear();
304                         target->GetImages().FindModules (&module_spec, NULL, NULL, NULL, matching_modules);
305                         num_matches += matching_modules.FindFunctions (name, eFunctionNameTypeBase, append, sc_list);
306                     }
307                 }
308             }
309             else
310             {
311                 num_matches = target->GetImages().FindFunctions (name, eFunctionNameTypeBase, append, sc_list);
312             }
313 
314             SymbolContext sc;
315 
316             if (num_matches == 0)
317             {
318                 result.AppendErrorWithFormat("Could not find function named: \"%s\".\n", m_options.symbol_name.c_str());
319                 result.SetStatus (eReturnStatusFailed);
320                 return false;
321             }
322 
323             sc_list.GetContextAtIndex (0, sc);
324             FileSpec start_file;
325             uint32_t start_line;
326             uint32_t end_line;
327             FileSpec end_file;
328             if (sc.function != NULL)
329             {
330                 sc.function->GetStartLineSourceInfo (start_file, start_line);
331                 if (start_line == 0)
332                 {
333                     result.AppendErrorWithFormat("Could not find line information for start of function: \"%s\".\n", m_options.symbol_name.c_str());
334                     result.SetStatus (eReturnStatusFailed);
335                     return false;
336                 }
337                 sc.function->GetEndLineSourceInfo (end_file, end_line);
338             }
339             else
340             {
341                 result.AppendErrorWithFormat("Could not find function info for: \"%s\".\n", m_options.symbol_name.c_str());
342                 result.SetStatus (eReturnStatusFailed);
343                 return false;
344             }
345 
346             if (num_matches > 1)
347             {
348                 // This could either be because there are multiple functions of this name, in which case
349                 // we'll have to specify this further...  Or it could be because there are multiple inlined instances
350                 // of one function.  So run through the matches and if they all have the same file & line then we can just
351                 // list one.
352 
353                 bool found_multiple = false;
354 
355                 for (size_t i = 1; i < num_matches; i++)
356                 {
357                     SymbolContext scratch_sc;
358                     sc_list.GetContextAtIndex (i, scratch_sc);
359                     if (scratch_sc.function != NULL)
360                     {
361                         FileSpec scratch_file;
362                         uint32_t scratch_line;
363                         scratch_sc.function->GetStartLineSourceInfo (scratch_file, scratch_line);
364                         if (scratch_file != start_file
365                             || scratch_line != start_line)
366                         {
367                             found_multiple = true;
368                             break;
369                         }
370                     }
371                 }
372                 if (found_multiple)
373                 {
374                     StreamString s;
375                     for (size_t i = 0; i < num_matches; i++)
376                     {
377                         SymbolContext scratch_sc;
378                         sc_list.GetContextAtIndex (i, scratch_sc);
379                         if (scratch_sc.function != NULL)
380                         {
381                             s.Printf("\n%d: ", i);
382                             scratch_sc.function->Dump (&s, true);
383                         }
384                     }
385                     result.AppendErrorWithFormat("Multiple functions found matching: %s: \n%s\n",
386                                                  m_options.symbol_name.c_str(),
387                                                  s.GetData());
388                     result.SetStatus (eReturnStatusFailed);
389                     return false;
390                 }
391             }
392 
393 
394             // This is a little hacky, but the first line table entry for a function points to the "{" that
395             // starts the function block.  It would be nice to actually get the function
396             // declaration in there too.  So back up a bit, but not further than what you're going to display.
397             size_t lines_to_back_up = m_options.num_lines >= 10 ? 5 : m_options.num_lines/2;
398             uint32_t line_no;
399             if (start_line <= lines_to_back_up)
400                 line_no = 1;
401             else
402                 line_no = start_line - lines_to_back_up;
403 
404             // For fun, if the function is shorter than the number of lines we're supposed to display,
405             // only display the function...
406             if (end_line != 0)
407             {
408                 if (m_options.num_lines > end_line - line_no)
409                     m_options.num_lines = end_line - line_no;
410             }
411 
412             char path_buf[PATH_MAX+1];
413             start_file.GetPath(path_buf, PATH_MAX);
414             result.AppendMessageWithFormat("File: %s.\n", path_buf);
415             m_interpreter.GetDebugger().GetSourceManager().DisplaySourceLinesWithLineNumbers (start_file,
416                                                                                               line_no,
417                                                                                               0,
418                                                                                               m_options.num_lines,
419                                                                                               "",
420                                                                                               &result.GetOutputStream());
421 
422             result.SetStatus (eReturnStatusSuccessFinishResult);
423             return true;
424 
425         }
426         else if (m_options.file_name.empty())
427         {
428             // Last valid source manager context, or the current frame if no
429             // valid last context in source manager.
430             // One little trick here, if you type the exact same list command twice in a row, it is
431             // more likely because you typed it once, then typed it again
432             if (m_options.start_line == 0)
433             {
434                 if (m_interpreter.GetDebugger().GetSourceManager().DisplayMoreWithLineNumbers (&result.GetOutputStream()))
435                 {
436                     result.SetStatus (eReturnStatusSuccessFinishResult);
437                 }
438             }
439             else
440             {
441                 if (m_interpreter.GetDebugger().GetSourceManager().DisplaySourceLinesWithLineNumbersUsingLastFile(
442                             m_options.start_line,   // Line to display
443                             0,                      // Lines before line to display
444                             m_options.num_lines,    // Lines after line to display
445                             "",                     // Don't mark "line"
446                             &result.GetOutputStream()))
447                 {
448                     result.SetStatus (eReturnStatusSuccessFinishResult);
449                 }
450 
451             }
452         }
453         else
454         {
455             const char *filename = m_options.file_name.c_str();
456             Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
457             if (target == NULL)
458             {
459                 result.AppendError ("invalid target, set executable file using 'file' command");
460                 result.SetStatus (eReturnStatusFailed);
461                 return false;
462             }
463 
464 
465             bool check_inlines = false;
466             SymbolContextList sc_list;
467             size_t num_matches = 0;
468 
469             if (m_options.m_modules.size() > 0)
470             {
471                 ModuleList matching_modules;
472                 for (unsigned i = 0, e = m_options.m_modules.size(); i != e; i++)
473                 {
474                     FileSpec module_spec(m_options.m_modules[i].c_str());
475                     if (module_spec)
476                     {
477                         matching_modules.Clear();
478                         target->GetImages().FindModules (&module_spec, NULL, NULL, NULL, matching_modules);
479                         num_matches += matching_modules.ResolveSymbolContextForFilePath (filename,
480                                                                                    0,
481                                                                                    check_inlines,
482                                                                                    eSymbolContextModule | eSymbolContextCompUnit,
483                                                                                    sc_list);
484                     }
485                 }
486             }
487             else
488             {
489                 num_matches = target->GetImages().ResolveSymbolContextForFilePath (filename,
490                                                                                    0,
491                                                                                    check_inlines,
492                                                                                    eSymbolContextModule | eSymbolContextCompUnit,
493                                                                                    sc_list);
494             }
495 
496             if (num_matches == 0)
497             {
498                 result.AppendErrorWithFormat("Could not find source file \"%s\".\n",
499                                              m_options.file_name.c_str());
500                 result.SetStatus (eReturnStatusFailed);
501                 return false;
502             }
503 
504             if (num_matches > 1)
505             {
506                 SymbolContext sc;
507                 bool got_multiple = false;
508                 FileSpec *test_cu_spec = NULL;
509 
510                 for (unsigned i = 0; i < num_matches; i++)
511                 {
512                     sc_list.GetContextAtIndex(i, sc);
513                     if (sc.comp_unit)
514                     {
515                         if (test_cu_spec)
516                         {
517                             if (test_cu_spec != static_cast<FileSpec *> (sc.comp_unit))
518                                 got_multiple = true;
519                                 break;
520                         }
521                         else
522                             test_cu_spec = sc.comp_unit;
523                     }
524                 }
525                 if (got_multiple)
526                 {
527                     result.AppendErrorWithFormat("Multiple source files found matching: \"%s.\"\n",
528                                                  m_options.file_name.c_str());
529                     result.SetStatus (eReturnStatusFailed);
530                     return false;
531                 }
532             }
533 
534             SymbolContext sc;
535             if (sc_list.GetContextAtIndex(0, sc))
536             {
537                 if (sc.comp_unit)
538                 {
539                     m_interpreter.GetDebugger().GetSourceManager().DisplaySourceLinesWithLineNumbers (sc.comp_unit,
540                                                                                                       m_options.start_line,
541                                                                                                       0,
542                                                                                                       m_options.num_lines,
543                                                                                                       "",
544                                                                                                       &result.GetOutputStream());
545 
546                     result.SetStatus (eReturnStatusSuccessFinishResult);
547                 }
548                 else
549                 {
550                     result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n",
551                                                  m_options.file_name.c_str());
552                     result.SetStatus (eReturnStatusFailed);
553                     return false;
554                 }
555             }
556         }
557         return result.Succeeded();
558     }
559 
560     virtual const char *GetRepeatCommand (Args &current_command_args, uint32_t index)
561     {
562         return m_cmd_name.c_str();
563     }
564 
565 protected:
566     CommandOptions m_options;
567 
568 };
569 
570 lldb::OptionDefinition
571 CommandObjectSourceList::CommandOptions::g_option_table[] =
572 {
573 { LLDB_OPT_SET_ALL, false, "count",    'c', required_argument, NULL, 0, eArgTypeCount,   "The number of source lines to display."},
574 { LLDB_OPT_SET_ALL, false, "shlib",    's', required_argument, NULL, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Look up the source file in the given shared library."},
575 { LLDB_OPT_SET_1, false, "file",       'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename,    "The file from which to display source."},
576 { LLDB_OPT_SET_1, false, "line",       'l', required_argument, NULL, 0, eArgTypeLineNum,    "The line number at which to start the display source."},
577 { LLDB_OPT_SET_2, false, "name",       'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, eArgTypeSymbol,    "The name of a function whose source to display."},
578 { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
579 };
580 
581 #pragma mark CommandObjectMultiwordSource
582 
583 //-------------------------------------------------------------------------
584 // CommandObjectMultiwordSource
585 //-------------------------------------------------------------------------
586 
587 CommandObjectMultiwordSource::CommandObjectMultiwordSource (CommandInterpreter &interpreter) :
588     CommandObjectMultiword (interpreter,
589                             "source",
590                             "A set of commands for accessing source file information",
591                             "source <subcommand> [<subcommand-options>]")
592 {
593     LoadSubCommand ("info",   CommandObjectSP (new CommandObjectSourceInfo (interpreter)));
594     LoadSubCommand ("list",   CommandObjectSP (new CommandObjectSourceList (interpreter)));
595 }
596 
597 CommandObjectMultiwordSource::~CommandObjectMultiwordSource ()
598 {
599 }
600 
601