1 //===-- CommandObjectLog.cpp ----------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "CommandObjectLog.h"
10 #include "lldb/Core/Debugger.h"
11 #include "lldb/Host/OptionParser.h"
12 #include "lldb/Interpreter/CommandReturnObject.h"
13 #include "lldb/Interpreter/OptionArgParser.h"
14 #include "lldb/Interpreter/OptionValueEnumeration.h"
15 #include "lldb/Interpreter/OptionValueUInt64.h"
16 #include "lldb/Interpreter/Options.h"
17 #include "lldb/Utility/Args.h"
18 #include "lldb/Utility/FileSpec.h"
19 #include "lldb/Utility/Log.h"
20 #include "lldb/Utility/Stream.h"
21 #include "lldb/Utility/Timer.h"
22 
23 using namespace lldb;
24 using namespace lldb_private;
25 
26 static constexpr OptionEnumValueElement g_log_handler_type[] = {
27     {
28         eLogHandlerDefault,
29         "default",
30         "Use the default (stream) log handler",
31     },
32     {
33         eLogHandlerStream,
34         "stream",
35         "Write log messages to the debugger output stream or to a file if one "
36         "is specified. A buffer size (in bytes) can be specified with -b. If "
37         "no buffer size is specified the output is unbuffered.",
38     },
39     {
40         eLogHandlerCircular,
41         "circular",
42         "Write log messages to a fixed size circular buffer. A buffer size "
43         "(number of messages) must be specified with -b.",
44     },
45     {
46         eLogHandlerSystem,
47         "os",
48         "Write log messages to the operating system log.",
49     },
50 };
51 
52 static constexpr OptionEnumValues LogHandlerType() {
53   return OptionEnumValues(g_log_handler_type);
54 }
55 
56 #define LLDB_OPTIONS_log_enable
57 #include "CommandOptions.inc"
58 
59 /// Common completion logic for log enable/disable.
60 static void CompleteEnableDisable(CompletionRequest &request) {
61   size_t arg_index = request.GetCursorIndex();
62   if (arg_index == 0) { // We got: log enable/disable x[tab]
63     for (llvm::StringRef channel : Log::ListChannels())
64       request.TryCompleteCurrentArg(channel);
65   } else if (arg_index >= 1) { // We got: log enable/disable channel x[tab]
66     llvm::StringRef channel = request.GetParsedLine().GetArgumentAtIndex(0);
67     Log::ForEachChannelCategory(
68         channel, [&request](llvm::StringRef name, llvm::StringRef desc) {
69           request.TryCompleteCurrentArg(name, desc);
70         });
71   }
72 }
73 
74 class CommandObjectLogEnable : public CommandObjectParsed {
75 public:
76   // Constructors and Destructors
77   CommandObjectLogEnable(CommandInterpreter &interpreter)
78       : CommandObjectParsed(interpreter, "log enable",
79                             "Enable logging for a single log channel.",
80                             nullptr) {
81     CommandArgumentEntry arg1;
82     CommandArgumentEntry arg2;
83     CommandArgumentData channel_arg;
84     CommandArgumentData category_arg;
85 
86     // Define the first (and only) variant of this arg.
87     channel_arg.arg_type = eArgTypeLogChannel;
88     channel_arg.arg_repetition = eArgRepeatPlain;
89 
90     // There is only one variant this argument could be; put it into the
91     // argument entry.
92     arg1.push_back(channel_arg);
93 
94     category_arg.arg_type = eArgTypeLogCategory;
95     category_arg.arg_repetition = eArgRepeatPlus;
96 
97     arg2.push_back(category_arg);
98 
99     // Push the data for the first argument into the m_arguments vector.
100     m_arguments.push_back(arg1);
101     m_arguments.push_back(arg2);
102   }
103 
104   ~CommandObjectLogEnable() override = default;
105 
106   Options *GetOptions() override { return &m_options; }
107 
108   class CommandOptions : public Options {
109   public:
110     CommandOptions() = default;
111 
112     ~CommandOptions() override = default;
113 
114     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
115                           ExecutionContext *execution_context) override {
116       Status error;
117       const int short_option = m_getopt_table[option_idx].val;
118 
119       switch (short_option) {
120       case 'f':
121         log_file.SetFile(option_arg, FileSpec::Style::native);
122         FileSystem::Instance().Resolve(log_file);
123         break;
124       case 'h':
125         handler = (LogHandlerKind)OptionArgParser::ToOptionEnum(
126             option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
127         if (!error.Success())
128           error.SetErrorStringWithFormat(
129               "unrecognized value for log handler '%s'",
130               option_arg.str().c_str());
131         break;
132       case 'b':
133         error =
134             buffer_size.SetValueFromString(option_arg, eVarSetOperationAssign);
135         break;
136       case 'v':
137         log_options |= LLDB_LOG_OPTION_VERBOSE;
138         break;
139       case 's':
140         log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE;
141         break;
142       case 'T':
143         log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP;
144         break;
145       case 'p':
146         log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD;
147         break;
148       case 'n':
149         log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME;
150         break;
151       case 'S':
152         log_options |= LLDB_LOG_OPTION_BACKTRACE;
153         break;
154       case 'a':
155         log_options |= LLDB_LOG_OPTION_APPEND;
156         break;
157       case 'F':
158         log_options |= LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION;
159         break;
160       default:
161         llvm_unreachable("Unimplemented option");
162       }
163 
164       return error;
165     }
166 
167     void OptionParsingStarting(ExecutionContext *execution_context) override {
168       log_file.Clear();
169       buffer_size.Clear();
170       handler = eLogHandlerStream;
171       log_options = 0;
172     }
173 
174     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
175       return llvm::makeArrayRef(g_log_enable_options);
176     }
177 
178     FileSpec log_file;
179     OptionValueUInt64 buffer_size;
180     LogHandlerKind handler = eLogHandlerStream;
181     uint32_t log_options = 0;
182   };
183 
184   void
185   HandleArgumentCompletion(CompletionRequest &request,
186                            OptionElementVector &opt_element_vector) override {
187     CompleteEnableDisable(request);
188   }
189 
190 protected:
191   bool DoExecute(Args &args, CommandReturnObject &result) override {
192     if (args.GetArgumentCount() < 2) {
193       result.AppendErrorWithFormat(
194           "%s takes a log channel and one or more log types.\n",
195           m_cmd_name.c_str());
196       return false;
197     }
198 
199     if (m_options.handler == eLogHandlerCircular &&
200         m_options.buffer_size.GetCurrentValue() == 0) {
201       result.AppendError(
202           "the circular buffer handler requires a non-zero buffer size.\n");
203       return false;
204     }
205 
206     // Store into a std::string since we're about to shift the channel off.
207     const std::string channel = std::string(args[0].ref());
208     args.Shift(); // Shift off the channel
209     char log_file[PATH_MAX];
210     if (m_options.log_file)
211       m_options.log_file.GetPath(log_file, sizeof(log_file));
212     else
213       log_file[0] = '\0';
214 
215     std::string error;
216     llvm::raw_string_ostream error_stream(error);
217     bool success = GetDebugger().EnableLog(
218         channel, args.GetArgumentArrayRef(), log_file, m_options.log_options,
219         m_options.buffer_size.GetCurrentValue(), m_options.handler,
220         error_stream);
221     result.GetErrorStream() << error_stream.str();
222 
223     if (success)
224       result.SetStatus(eReturnStatusSuccessFinishNoResult);
225     else
226       result.SetStatus(eReturnStatusFailed);
227     return result.Succeeded();
228   }
229 
230   CommandOptions m_options;
231 };
232 
233 class CommandObjectLogDisable : public CommandObjectParsed {
234 public:
235   // Constructors and Destructors
236   CommandObjectLogDisable(CommandInterpreter &interpreter)
237       : CommandObjectParsed(interpreter, "log disable",
238                             "Disable one or more log channel categories.",
239                             nullptr) {
240     CommandArgumentEntry arg1;
241     CommandArgumentEntry arg2;
242     CommandArgumentData channel_arg;
243     CommandArgumentData category_arg;
244 
245     // Define the first (and only) variant of this arg.
246     channel_arg.arg_type = eArgTypeLogChannel;
247     channel_arg.arg_repetition = eArgRepeatPlain;
248 
249     // There is only one variant this argument could be; put it into the
250     // argument entry.
251     arg1.push_back(channel_arg);
252 
253     category_arg.arg_type = eArgTypeLogCategory;
254     category_arg.arg_repetition = eArgRepeatPlus;
255 
256     arg2.push_back(category_arg);
257 
258     // Push the data for the first argument into the m_arguments vector.
259     m_arguments.push_back(arg1);
260     m_arguments.push_back(arg2);
261   }
262 
263   ~CommandObjectLogDisable() override = default;
264 
265   void
266   HandleArgumentCompletion(CompletionRequest &request,
267                            OptionElementVector &opt_element_vector) override {
268     CompleteEnableDisable(request);
269   }
270 
271 protected:
272   bool DoExecute(Args &args, CommandReturnObject &result) override {
273     if (args.empty()) {
274       result.AppendErrorWithFormat(
275           "%s takes a log channel and one or more log types.\n",
276           m_cmd_name.c_str());
277       return false;
278     }
279 
280     const std::string channel = std::string(args[0].ref());
281     args.Shift(); // Shift off the channel
282     if (channel == "all") {
283       Log::DisableAllLogChannels();
284       result.SetStatus(eReturnStatusSuccessFinishNoResult);
285     } else {
286       std::string error;
287       llvm::raw_string_ostream error_stream(error);
288       if (Log::DisableLogChannel(channel, args.GetArgumentArrayRef(),
289                                  error_stream))
290         result.SetStatus(eReturnStatusSuccessFinishNoResult);
291       result.GetErrorStream() << error_stream.str();
292     }
293     return result.Succeeded();
294   }
295 };
296 
297 class CommandObjectLogList : public CommandObjectParsed {
298 public:
299   // Constructors and Destructors
300   CommandObjectLogList(CommandInterpreter &interpreter)
301       : CommandObjectParsed(interpreter, "log list",
302                             "List the log categories for one or more log "
303                             "channels.  If none specified, lists them all.",
304                             nullptr) {
305     CommandArgumentEntry arg;
306     CommandArgumentData channel_arg;
307 
308     // Define the first (and only) variant of this arg.
309     channel_arg.arg_type = eArgTypeLogChannel;
310     channel_arg.arg_repetition = eArgRepeatStar;
311 
312     // There is only one variant this argument could be; put it into the
313     // argument entry.
314     arg.push_back(channel_arg);
315 
316     // Push the data for the first argument into the m_arguments vector.
317     m_arguments.push_back(arg);
318   }
319 
320   ~CommandObjectLogList() override = default;
321 
322   void
323   HandleArgumentCompletion(CompletionRequest &request,
324                            OptionElementVector &opt_element_vector) override {
325     for (llvm::StringRef channel : Log::ListChannels())
326       request.TryCompleteCurrentArg(channel);
327   }
328 
329 protected:
330   bool DoExecute(Args &args, CommandReturnObject &result) override {
331     std::string output;
332     llvm::raw_string_ostream output_stream(output);
333     if (args.empty()) {
334       Log::ListAllLogChannels(output_stream);
335       result.SetStatus(eReturnStatusSuccessFinishResult);
336     } else {
337       bool success = true;
338       for (const auto &entry : args.entries())
339         success =
340             success && Log::ListChannelCategories(entry.ref(), output_stream);
341       if (success)
342         result.SetStatus(eReturnStatusSuccessFinishResult);
343     }
344     result.GetOutputStream() << output_stream.str();
345     return result.Succeeded();
346   }
347 };
348 
349 class CommandObjectLogTimerEnable : public CommandObjectParsed {
350 public:
351   // Constructors and Destructors
352   CommandObjectLogTimerEnable(CommandInterpreter &interpreter)
353       : CommandObjectParsed(interpreter, "log timers enable",
354                             "enable LLDB internal performance timers",
355                             "log timers enable <depth>") {
356     CommandArgumentEntry arg;
357     CommandArgumentData depth_arg;
358 
359     // Define the first (and only) variant of this arg.
360     depth_arg.arg_type = eArgTypeCount;
361     depth_arg.arg_repetition = eArgRepeatOptional;
362 
363     // There is only one variant this argument could be; put it into the
364     // argument entry.
365     arg.push_back(depth_arg);
366 
367     // Push the data for the first argument into the m_arguments vector.
368     m_arguments.push_back(arg);
369   }
370 
371   ~CommandObjectLogTimerEnable() override = default;
372 
373 protected:
374   bool DoExecute(Args &args, CommandReturnObject &result) override {
375     result.SetStatus(eReturnStatusFailed);
376 
377     if (args.GetArgumentCount() == 0) {
378       Timer::SetDisplayDepth(UINT32_MAX);
379       result.SetStatus(eReturnStatusSuccessFinishNoResult);
380     } else if (args.GetArgumentCount() == 1) {
381       uint32_t depth;
382       if (args[0].ref().consumeInteger(0, depth)) {
383         result.AppendError(
384             "Could not convert enable depth to an unsigned integer.");
385       } else {
386         Timer::SetDisplayDepth(depth);
387         result.SetStatus(eReturnStatusSuccessFinishNoResult);
388       }
389     }
390 
391     if (!result.Succeeded()) {
392       result.AppendError("Missing subcommand");
393       result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
394     }
395     return result.Succeeded();
396   }
397 };
398 
399 class CommandObjectLogTimerDisable : public CommandObjectParsed {
400 public:
401   // Constructors and Destructors
402   CommandObjectLogTimerDisable(CommandInterpreter &interpreter)
403       : CommandObjectParsed(interpreter, "log timers disable",
404                             "disable LLDB internal performance timers",
405                             nullptr) {}
406 
407   ~CommandObjectLogTimerDisable() override = default;
408 
409 protected:
410   bool DoExecute(Args &args, CommandReturnObject &result) override {
411     Timer::DumpCategoryTimes(&result.GetOutputStream());
412     Timer::SetDisplayDepth(0);
413     result.SetStatus(eReturnStatusSuccessFinishResult);
414 
415     if (!result.Succeeded()) {
416       result.AppendError("Missing subcommand");
417       result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
418     }
419     return result.Succeeded();
420   }
421 };
422 
423 class CommandObjectLogTimerDump : public CommandObjectParsed {
424 public:
425   // Constructors and Destructors
426   CommandObjectLogTimerDump(CommandInterpreter &interpreter)
427       : CommandObjectParsed(interpreter, "log timers dump",
428                             "dump LLDB internal performance timers", nullptr) {}
429 
430   ~CommandObjectLogTimerDump() override = default;
431 
432 protected:
433   bool DoExecute(Args &args, CommandReturnObject &result) override {
434     Timer::DumpCategoryTimes(&result.GetOutputStream());
435     result.SetStatus(eReturnStatusSuccessFinishResult);
436 
437     if (!result.Succeeded()) {
438       result.AppendError("Missing subcommand");
439       result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
440     }
441     return result.Succeeded();
442   }
443 };
444 
445 class CommandObjectLogTimerReset : public CommandObjectParsed {
446 public:
447   // Constructors and Destructors
448   CommandObjectLogTimerReset(CommandInterpreter &interpreter)
449       : CommandObjectParsed(interpreter, "log timers reset",
450                             "reset LLDB internal performance timers", nullptr) {
451   }
452 
453   ~CommandObjectLogTimerReset() override = default;
454 
455 protected:
456   bool DoExecute(Args &args, CommandReturnObject &result) override {
457     Timer::ResetCategoryTimes();
458     result.SetStatus(eReturnStatusSuccessFinishResult);
459 
460     if (!result.Succeeded()) {
461       result.AppendError("Missing subcommand");
462       result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
463     }
464     return result.Succeeded();
465   }
466 };
467 
468 class CommandObjectLogTimerIncrement : public CommandObjectParsed {
469 public:
470   // Constructors and Destructors
471   CommandObjectLogTimerIncrement(CommandInterpreter &interpreter)
472       : CommandObjectParsed(interpreter, "log timers increment",
473                             "increment LLDB internal performance timers",
474                             "log timers increment <bool>") {
475     CommandArgumentEntry arg;
476     CommandArgumentData bool_arg;
477 
478     // Define the first (and only) variant of this arg.
479     bool_arg.arg_type = eArgTypeBoolean;
480     bool_arg.arg_repetition = eArgRepeatPlain;
481 
482     // There is only one variant this argument could be; put it into the
483     // argument entry.
484     arg.push_back(bool_arg);
485 
486     // Push the data for the first argument into the m_arguments vector.
487     m_arguments.push_back(arg);
488   }
489 
490   ~CommandObjectLogTimerIncrement() override = default;
491 
492   void
493   HandleArgumentCompletion(CompletionRequest &request,
494                            OptionElementVector &opt_element_vector) override {
495     request.TryCompleteCurrentArg("true");
496     request.TryCompleteCurrentArg("false");
497   }
498 
499 protected:
500   bool DoExecute(Args &args, CommandReturnObject &result) override {
501     result.SetStatus(eReturnStatusFailed);
502 
503     if (args.GetArgumentCount() == 1) {
504       bool success;
505       bool increment =
506           OptionArgParser::ToBoolean(args[0].ref(), false, &success);
507 
508       if (success) {
509         Timer::SetQuiet(!increment);
510         result.SetStatus(eReturnStatusSuccessFinishNoResult);
511       } else
512         result.AppendError("Could not convert increment value to boolean.");
513     }
514 
515     if (!result.Succeeded()) {
516       result.AppendError("Missing subcommand");
517       result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
518     }
519     return result.Succeeded();
520   }
521 };
522 
523 class CommandObjectLogTimer : public CommandObjectMultiword {
524 public:
525   CommandObjectLogTimer(CommandInterpreter &interpreter)
526       : CommandObjectMultiword(interpreter, "log timers",
527                                "Enable, disable, dump, and reset LLDB internal "
528                                "performance timers.",
529                                "log timers < enable <depth> | disable | dump | "
530                                "increment <bool> | reset >") {
531     LoadSubCommand("enable", CommandObjectSP(
532                                  new CommandObjectLogTimerEnable(interpreter)));
533     LoadSubCommand("disable", CommandObjectSP(new CommandObjectLogTimerDisable(
534                                   interpreter)));
535     LoadSubCommand("dump",
536                    CommandObjectSP(new CommandObjectLogTimerDump(interpreter)));
537     LoadSubCommand(
538         "reset", CommandObjectSP(new CommandObjectLogTimerReset(interpreter)));
539     LoadSubCommand(
540         "increment",
541         CommandObjectSP(new CommandObjectLogTimerIncrement(interpreter)));
542   }
543 
544   ~CommandObjectLogTimer() override = default;
545 };
546 
547 CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter)
548     : CommandObjectMultiword(interpreter, "log",
549                              "Commands controlling LLDB internal logging.",
550                              "log <subcommand> [<command-options>]") {
551   LoadSubCommand("enable",
552                  CommandObjectSP(new CommandObjectLogEnable(interpreter)));
553   LoadSubCommand("disable",
554                  CommandObjectSP(new CommandObjectLogDisable(interpreter)));
555   LoadSubCommand("list",
556                  CommandObjectSP(new CommandObjectLogList(interpreter)));
557   LoadSubCommand("timers",
558                  CommandObjectSP(new CommandObjectLogTimer(interpreter)));
559 }
560 
561 CommandObjectLog::~CommandObjectLog() = default;
562