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