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