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