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