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