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