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