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