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