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