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