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