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