1 //===-- CommandObjectLog.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 "CommandObjectLog.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/lldb-private-log.h"
17 
18 #include "lldb/Interpreter/Args.h"
19 #include "lldb/Core/Debugger.h"
20 #include "lldb/Host/FileSpec.h"
21 #include "lldb/Core/Log.h"
22 #include "lldb/Core/Module.h"
23 #include "lldb/Interpreter/Options.h"
24 #include "lldb/Core/RegularExpression.h"
25 #include "lldb/Core/Stream.h"
26 #include "lldb/Core/StreamFile.h"
27 #include "lldb/Core/Timer.h"
28 
29 #include "lldb/Core/Debugger.h"
30 #include "lldb/Interpreter/CommandInterpreter.h"
31 #include "lldb/Interpreter/CommandReturnObject.h"
32 
33 #include "lldb/Symbol/LineTable.h"
34 #include "lldb/Symbol/ObjectFile.h"
35 #include "lldb/Symbol/SymbolFile.h"
36 #include "lldb/Symbol/SymbolVendor.h"
37 
38 #include "lldb/Target/Process.h"
39 #include "lldb/Target/Target.h"
40 
41 using namespace lldb;
42 using namespace lldb_private;
43 
44 
45 class CommandObjectLogEnable : public CommandObjectParsed
46 {
47 public:
48     //------------------------------------------------------------------
49     // Constructors and Destructors
50     //------------------------------------------------------------------
51     CommandObjectLogEnable(CommandInterpreter &interpreter) :
52         CommandObjectParsed (interpreter,
53                              "log enable",
54                              "Enable logging for a single log channel.",
55                              NULL),
56         m_options (interpreter)
57     {
58 
59         CommandArgumentEntry arg1;
60         CommandArgumentEntry arg2;
61         CommandArgumentData channel_arg;
62         CommandArgumentData category_arg;
63 
64         // Define the first (and only) variant of this arg.
65         channel_arg.arg_type = eArgTypeLogChannel;
66         channel_arg.arg_repetition = eArgRepeatPlain;
67 
68         // There is only one variant this argument could be; put it into the argument entry.
69         arg1.push_back (channel_arg);
70 
71         category_arg.arg_type = eArgTypeLogCategory;
72         category_arg.arg_repetition = eArgRepeatPlus;
73 
74         arg2.push_back (category_arg);
75 
76         // Push the data for the first argument into the m_arguments vector.
77         m_arguments.push_back (arg1);
78         m_arguments.push_back (arg2);
79     }
80 
81     virtual
82     ~CommandObjectLogEnable()
83     {
84     }
85 
86     Options *
87     GetOptions ()
88     {
89         return &m_options;
90     }
91 
92 //    int
93 //    HandleArgumentCompletion (Args &input,
94 //                              int &cursor_index,
95 //                              int &cursor_char_position,
96 //                              OptionElementVector &opt_element_vector,
97 //                              int match_start_point,
98 //                              int max_return_elements,
99 //                              bool &word_complete,
100 //                              StringList &matches)
101 //    {
102 //        std::string completion_str (input.GetArgumentAtIndex(cursor_index));
103 //        completion_str.erase (cursor_char_position);
104 //
105 //        if (cursor_index == 1)
106 //        {
107 //            //
108 //            Log::AutoCompleteChannelName (completion_str.c_str(), matches);
109 //        }
110 //        return matches.GetSize();
111 //    }
112 //
113 
114     class CommandOptions : public Options
115     {
116     public:
117 
118         CommandOptions (CommandInterpreter &interpreter) :
119             Options (interpreter),
120             log_file (),
121             log_options (0)
122         {
123         }
124 
125 
126         virtual
127         ~CommandOptions ()
128         {
129         }
130 
131         virtual Error
132         SetOptionValue (uint32_t option_idx, const char *option_arg)
133         {
134             Error error;
135             char short_option = (char) m_getopt_table[option_idx].val;
136 
137             switch (short_option)
138             {
139             case 'f':  log_file = option_arg;                                 break;
140             case 't':  log_options |= LLDB_LOG_OPTION_THREADSAFE;             break;
141             case 'v':  log_options |= LLDB_LOG_OPTION_VERBOSE;                break;
142             case 'g':  log_options |= LLDB_LOG_OPTION_DEBUG;                  break;
143             case 's':  log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE;       break;
144             case 'T':  log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP;      break;
145             case 'p':  log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD;break;
146             case 'n':  log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME;    break;
147             case 'S':  log_options |= LLDB_LOG_OPTION_BACKTRACE;              break;
148             default:
149                 error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option);
150                 break;
151             }
152 
153             return error;
154         }
155 
156         void
157         OptionParsingStarting ()
158         {
159             log_file.clear();
160             log_options = 0;
161         }
162 
163         const OptionDefinition*
164         GetDefinitions ()
165         {
166             return g_option_table;
167         }
168 
169         // Options table: Required for subclasses of Options.
170 
171         static OptionDefinition g_option_table[];
172 
173         // Instance variables to hold the values for command options.
174 
175         std::string log_file;
176         uint32_t log_options;
177     };
178 
179 protected:
180     virtual bool
181     DoExecute (Args& args,
182              CommandReturnObject &result)
183     {
184         if (args.GetArgumentCount() < 2)
185         {
186             result.AppendErrorWithFormat("%s takes a log channel and one or more log types.\n", m_cmd_name.c_str());
187         }
188         else
189         {
190             std::string channel(args.GetArgumentAtIndex(0));
191             args.Shift ();  // Shift off the channel
192             bool success = m_interpreter.GetDebugger().EnableLog (channel.c_str(),
193                                                                   args.GetConstArgumentVector(),
194                                                                   m_options.log_file.c_str(),
195                                                                   m_options.log_options,
196                                                                   result.GetErrorStream());
197             if (success)
198                 result.SetStatus (eReturnStatusSuccessFinishNoResult);
199             else
200                 result.SetStatus (eReturnStatusFailed);
201         }
202         return result.Succeeded();
203     }
204 
205     CommandOptions m_options;
206 };
207 
208 OptionDefinition
209 CommandObjectLogEnable::CommandOptions::g_option_table[] =
210 {
211 { LLDB_OPT_SET_1, false, "file",       'f', required_argument, NULL, 0, eArgTypeFilename,   "Set the destination file to log to."},
212 { LLDB_OPT_SET_1, false, "threadsafe", 't', no_argument,       NULL, 0, eArgTypeNone,        "Enable thread safe logging to avoid interweaved log lines." },
213 { LLDB_OPT_SET_1, false, "verbose",    'v', no_argument,       NULL, 0, eArgTypeNone,       "Enable verbose logging." },
214 { LLDB_OPT_SET_1, false, "debug",      'g', no_argument,       NULL, 0, eArgTypeNone,       "Enable debug logging." },
215 { LLDB_OPT_SET_1, false, "sequence",   's', no_argument,       NULL, 0, eArgTypeNone,       "Prepend all log lines with an increasing integer sequence id." },
216 { LLDB_OPT_SET_1, false, "timestamp",  'T', no_argument,       NULL, 0, eArgTypeNone,       "Prepend all log lines with a timestamp." },
217 { LLDB_OPT_SET_1, false, "pid-tid",    'p', no_argument,       NULL, 0, eArgTypeNone,       "Prepend all log lines with the process and thread ID that generates the log line." },
218 { LLDB_OPT_SET_1, false, "thread-name",'n', no_argument,       NULL, 0, eArgTypeNone,       "Prepend all log lines with the thread name for the thread that generates the log line." },
219 { LLDB_OPT_SET_1, false, "stack",      'S', no_argument,       NULL, 0, eArgTypeNone,       "Append a stack backtrace to each log line." },
220 { 0, false, NULL,                       0,  0,                 NULL, 0, eArgTypeNone,       NULL }
221 };
222 
223 class CommandObjectLogDisable : public CommandObjectParsed
224 {
225 public:
226     //------------------------------------------------------------------
227     // Constructors and Destructors
228     //------------------------------------------------------------------
229     CommandObjectLogDisable(CommandInterpreter &interpreter) :
230         CommandObjectParsed (interpreter,
231                              "log disable",
232                              "Disable one or more log channel categories.",
233                              NULL)
234     {
235         CommandArgumentEntry arg1;
236         CommandArgumentEntry arg2;
237         CommandArgumentData channel_arg;
238         CommandArgumentData category_arg;
239 
240         // Define the first (and only) variant of this arg.
241         channel_arg.arg_type = eArgTypeLogChannel;
242         channel_arg.arg_repetition = eArgRepeatPlain;
243 
244         // There is only one variant this argument could be; put it into the argument entry.
245         arg1.push_back (channel_arg);
246 
247         category_arg.arg_type = eArgTypeLogCategory;
248         category_arg.arg_repetition = eArgRepeatPlus;
249 
250         arg2.push_back (category_arg);
251 
252         // Push the data for the first argument into the m_arguments vector.
253         m_arguments.push_back (arg1);
254         m_arguments.push_back (arg2);
255     }
256 
257     virtual
258     ~CommandObjectLogDisable()
259     {
260     }
261 
262 protected:
263     virtual bool
264     DoExecute (Args& args,
265              CommandReturnObject &result)
266     {
267         const size_t argc = args.GetArgumentCount();
268         if (argc == 0)
269         {
270             result.AppendErrorWithFormat("%s takes a log channel and one or more log types.\n", m_cmd_name.c_str());
271         }
272         else
273         {
274             Log::Callbacks log_callbacks;
275 
276             std::string channel(args.GetArgumentAtIndex(0));
277             args.Shift ();  // Shift off the channel
278             if (Log::GetLogChannelCallbacks (channel.c_str(), log_callbacks))
279             {
280                 log_callbacks.disable (args.GetConstArgumentVector(), &result.GetErrorStream());
281                 result.SetStatus(eReturnStatusSuccessFinishNoResult);
282             }
283             else if (channel == "all")
284             {
285                 Log::DisableAllLogChannels(&result.GetErrorStream());
286             }
287             else
288             {
289                 LogChannelSP log_channel_sp (LogChannel::FindPlugin(channel.c_str()));
290                 if (log_channel_sp)
291                 {
292                     log_channel_sp->Disable(args.GetConstArgumentVector(), &result.GetErrorStream());
293                     result.SetStatus(eReturnStatusSuccessFinishNoResult);
294                 }
295                 else
296                     result.AppendErrorWithFormat("Invalid log channel '%s'.\n", args.GetArgumentAtIndex(0));
297             }
298         }
299         return result.Succeeded();
300     }
301 };
302 
303 class CommandObjectLogList : public CommandObjectParsed
304 {
305 public:
306     //------------------------------------------------------------------
307     // Constructors and Destructors
308     //------------------------------------------------------------------
309     CommandObjectLogList(CommandInterpreter &interpreter) :
310         CommandObjectParsed (interpreter,
311                              "log list",
312                              "List the log categories for one or more log channels.  If none specified, lists them all.",
313                              NULL)
314     {
315         CommandArgumentEntry arg;
316         CommandArgumentData channel_arg;
317 
318         // Define the first (and only) variant of this arg.
319         channel_arg.arg_type = eArgTypeLogChannel;
320         channel_arg.arg_repetition = eArgRepeatStar;
321 
322         // There is only one variant this argument could be; put it into the argument entry.
323         arg.push_back (channel_arg);
324 
325         // Push the data for the first argument into the m_arguments vector.
326         m_arguments.push_back (arg);
327     }
328 
329     virtual
330     ~CommandObjectLogList()
331     {
332     }
333 
334 protected:
335     virtual bool
336     DoExecute (Args& args,
337              CommandReturnObject &result)
338     {
339         const size_t argc = args.GetArgumentCount();
340         if (argc == 0)
341         {
342             Log::ListAllLogChannels (&result.GetOutputStream());
343             result.SetStatus(eReturnStatusSuccessFinishResult);
344         }
345         else
346         {
347             for (size_t i=0; i<argc; ++i)
348             {
349                 Log::Callbacks log_callbacks;
350 
351                 std::string channel(args.GetArgumentAtIndex(i));
352                 if (Log::GetLogChannelCallbacks (channel.c_str(), log_callbacks))
353                 {
354                     log_callbacks.list_categories (&result.GetOutputStream());
355                     result.SetStatus(eReturnStatusSuccessFinishResult);
356                 }
357                 else if (channel == "all")
358                 {
359                     Log::ListAllLogChannels (&result.GetOutputStream());
360                     result.SetStatus(eReturnStatusSuccessFinishResult);
361                 }
362                 else
363                 {
364                     LogChannelSP log_channel_sp (LogChannel::FindPlugin(channel.c_str()));
365                     if (log_channel_sp)
366                     {
367                         log_channel_sp->ListCategories(&result.GetOutputStream());
368                         result.SetStatus(eReturnStatusSuccessFinishNoResult);
369                     }
370                     else
371                         result.AppendErrorWithFormat("Invalid log channel '%s'.\n", args.GetArgumentAtIndex(0));
372                 }
373             }
374         }
375         return result.Succeeded();
376     }
377 };
378 
379 class CommandObjectLogTimer : public CommandObjectParsed
380 {
381 public:
382     //------------------------------------------------------------------
383     // Constructors and Destructors
384     //------------------------------------------------------------------
385     CommandObjectLogTimer(CommandInterpreter &interpreter) :
386         CommandObjectParsed (interpreter,
387                            "log timers",
388                            "Enable, disable, dump, and reset LLDB internal performance timers.",
389                            "log timers < enable <depth> | disable | dump | increment <bool> | reset >")
390     {
391     }
392 
393     virtual
394     ~CommandObjectLogTimer()
395     {
396     }
397 
398 protected:
399     virtual bool
400     DoExecute (Args& args,
401              CommandReturnObject &result)
402     {
403         const size_t argc = args.GetArgumentCount();
404         result.SetStatus(eReturnStatusFailed);
405 
406         if (argc == 1)
407         {
408             const char *sub_command = args.GetArgumentAtIndex(0);
409 
410             if (strcasecmp(sub_command, "enable") == 0)
411             {
412                 Timer::SetDisplayDepth (UINT32_MAX);
413                 result.SetStatus(eReturnStatusSuccessFinishNoResult);
414             }
415             else if (strcasecmp(sub_command, "disable") == 0)
416             {
417                 Timer::DumpCategoryTimes (&result.GetOutputStream());
418                 Timer::SetDisplayDepth (0);
419                 result.SetStatus(eReturnStatusSuccessFinishResult);
420             }
421             else if (strcasecmp(sub_command, "dump") == 0)
422             {
423                 Timer::DumpCategoryTimes (&result.GetOutputStream());
424                 result.SetStatus(eReturnStatusSuccessFinishResult);
425             }
426             else if (strcasecmp(sub_command, "reset") == 0)
427             {
428                 Timer::ResetCategoryTimes ();
429                 result.SetStatus(eReturnStatusSuccessFinishResult);
430             }
431 
432         }
433         else if (argc == 2)
434         {
435             const char *sub_command = args.GetArgumentAtIndex(0);
436 
437             if (strcasecmp(sub_command, "enable") == 0)
438             {
439                 bool success;
440                 uint32_t depth = Args::StringToUInt32(args.GetArgumentAtIndex(1), 0, 0, &success);
441                 if (success)
442                 {
443                     Timer::SetDisplayDepth (depth);
444                     result.SetStatus(eReturnStatusSuccessFinishNoResult);
445                 }
446                 else
447                     result.AppendError("Could not convert enable depth to an unsigned integer.");
448             }
449             if (strcasecmp(sub_command, "increment") == 0)
450             {
451                 bool success;
452                 bool increment = Args::StringToBoolean(args.GetArgumentAtIndex(1), false, &success);
453                 if (success)
454                 {
455                     Timer::SetQuiet (!increment);
456                     result.SetStatus(eReturnStatusSuccessFinishNoResult);
457                 }
458                 else
459                     result.AppendError("Could not convert increment value to boolean.");
460             }
461         }
462 
463         if (!result.Succeeded())
464         {
465             result.AppendError("Missing subcommand");
466             result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
467         }
468         return result.Succeeded();
469     }
470 };
471 
472 //----------------------------------------------------------------------
473 // CommandObjectLog constructor
474 //----------------------------------------------------------------------
475 CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter) :
476     CommandObjectMultiword (interpreter,
477                             "log",
478                             "A set of commands for operating on logs.",
479                             "log <command> [<command-options>]")
480 {
481     LoadSubCommand ("enable",  CommandObjectSP (new CommandObjectLogEnable (interpreter)));
482     LoadSubCommand ("disable", CommandObjectSP (new CommandObjectLogDisable (interpreter)));
483     LoadSubCommand ("list",    CommandObjectSP (new CommandObjectLogList (interpreter)));
484     LoadSubCommand ("timers",  CommandObjectSP (new CommandObjectLogTimer (interpreter)));
485 }
486 
487 //----------------------------------------------------------------------
488 // Destructor
489 //----------------------------------------------------------------------
490 CommandObjectLog::~CommandObjectLog()
491 {
492 }
493 
494 
495 
496 
497