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