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 void 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;
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;
178 }
179
180 if ((m_options.handler != eLogHandlerCircular &&
181 m_options.handler != eLogHandlerStream) &&
182 m_options.buffer_size.GetCurrentValue() != 0) {
183 result.AppendError("a buffer size can only be specified for the circular "
184 "and stream buffer handler.\n");
185 return;
186 }
187
188 if (m_options.handler != eLogHandlerStream && m_options.log_file) {
189 result.AppendError(
190 "a file name can only be specified for the stream handler.\n");
191 return;
192 }
193
194 // Store into a std::string since we're about to shift the channel off.
195 const std::string channel = std::string(args[0].ref());
196 args.Shift(); // Shift off the channel
197 char log_file[PATH_MAX];
198 if (m_options.log_file)
199 m_options.log_file.GetPath(log_file, sizeof(log_file));
200 else
201 log_file[0] = '\0';
202
203 std::string error;
204 llvm::raw_string_ostream error_stream(error);
205 bool success = GetDebugger().EnableLog(
206 channel, args.GetArgumentArrayRef(), log_file, m_options.log_options,
207 m_options.buffer_size.GetCurrentValue(), m_options.handler,
208 error_stream);
209 result.GetErrorStream() << error_stream.str();
210
211 if (success)
212 result.SetStatus(eReturnStatusSuccessFinishNoResult);
213 else
214 result.SetStatus(eReturnStatusFailed);
215 }
216
217 CommandOptions m_options;
218 };
219
220 class CommandObjectLogDisable : public CommandObjectParsed {
221 public:
222 // Constructors and Destructors
CommandObjectLogDisable(CommandInterpreter & interpreter)223 CommandObjectLogDisable(CommandInterpreter &interpreter)
224 : CommandObjectParsed(interpreter, "log disable",
225 "Disable one or more log channel categories.",
226 nullptr) {
227 CommandArgumentEntry arg1;
228 CommandArgumentEntry arg2;
229 CommandArgumentData channel_arg;
230 CommandArgumentData category_arg;
231
232 // Define the first (and only) variant of this arg.
233 channel_arg.arg_type = eArgTypeLogChannel;
234 channel_arg.arg_repetition = eArgRepeatPlain;
235
236 // There is only one variant this argument could be; put it into the
237 // argument entry.
238 arg1.push_back(channel_arg);
239
240 category_arg.arg_type = eArgTypeLogCategory;
241 category_arg.arg_repetition = eArgRepeatPlus;
242
243 arg2.push_back(category_arg);
244
245 // Push the data for the first argument into the m_arguments vector.
246 m_arguments.push_back(arg1);
247 m_arguments.push_back(arg2);
248 }
249
250 ~CommandObjectLogDisable() override = default;
251
252 void
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)253 HandleArgumentCompletion(CompletionRequest &request,
254 OptionElementVector &opt_element_vector) override {
255 CompleteEnableDisable(request);
256 }
257
258 protected:
DoExecute(Args & args,CommandReturnObject & result)259 void DoExecute(Args &args, CommandReturnObject &result) override {
260 if (args.empty()) {
261 result.AppendErrorWithFormat(
262 "%s takes a log channel and one or more log types.\n",
263 m_cmd_name.c_str());
264 return;
265 }
266
267 const std::string channel = std::string(args[0].ref());
268 args.Shift(); // Shift off the channel
269 if (channel == "all") {
270 Log::DisableAllLogChannels();
271 result.SetStatus(eReturnStatusSuccessFinishNoResult);
272 } else {
273 std::string error;
274 llvm::raw_string_ostream error_stream(error);
275 if (Log::DisableLogChannel(channel, args.GetArgumentArrayRef(),
276 error_stream))
277 result.SetStatus(eReturnStatusSuccessFinishNoResult);
278 result.GetErrorStream() << error_stream.str();
279 }
280 }
281 };
282
283 class CommandObjectLogList : public CommandObjectParsed {
284 public:
285 // Constructors and Destructors
CommandObjectLogList(CommandInterpreter & interpreter)286 CommandObjectLogList(CommandInterpreter &interpreter)
287 : CommandObjectParsed(interpreter, "log list",
288 "List the log categories for one or more log "
289 "channels. If none specified, lists them all.",
290 nullptr) {
291 CommandArgumentEntry arg;
292 CommandArgumentData channel_arg;
293
294 // Define the first (and only) variant of this arg.
295 channel_arg.arg_type = eArgTypeLogChannel;
296 channel_arg.arg_repetition = eArgRepeatStar;
297
298 // There is only one variant this argument could be; put it into the
299 // argument entry.
300 arg.push_back(channel_arg);
301
302 // Push the data for the first argument into the m_arguments vector.
303 m_arguments.push_back(arg);
304 }
305
306 ~CommandObjectLogList() override = default;
307
308 void
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)309 HandleArgumentCompletion(CompletionRequest &request,
310 OptionElementVector &opt_element_vector) override {
311 for (llvm::StringRef channel : Log::ListChannels())
312 request.TryCompleteCurrentArg(channel);
313 }
314
315 protected:
DoExecute(Args & args,CommandReturnObject & result)316 void DoExecute(Args &args, CommandReturnObject &result) override {
317 std::string output;
318 llvm::raw_string_ostream output_stream(output);
319 if (args.empty()) {
320 Log::ListAllLogChannels(output_stream);
321 result.SetStatus(eReturnStatusSuccessFinishResult);
322 } else {
323 bool success = true;
324 for (const auto &entry : args.entries())
325 success =
326 success && Log::ListChannelCategories(entry.ref(), output_stream);
327 if (success)
328 result.SetStatus(eReturnStatusSuccessFinishResult);
329 }
330 result.GetOutputStream() << output_stream.str();
331 }
332 };
333 class CommandObjectLogDump : public CommandObjectParsed {
334 public:
CommandObjectLogDump(CommandInterpreter & interpreter)335 CommandObjectLogDump(CommandInterpreter &interpreter)
336 : CommandObjectParsed(interpreter, "log dump",
337 "dump circular buffer logs", nullptr) {
338 CommandArgumentEntry arg1;
339 CommandArgumentData channel_arg;
340
341 // Define the first (and only) variant of this arg.
342 channel_arg.arg_type = eArgTypeLogChannel;
343 channel_arg.arg_repetition = eArgRepeatPlain;
344
345 // There is only one variant this argument could be; put it into the
346 // argument entry.
347 arg1.push_back(channel_arg);
348
349 // Push the data for the first argument into the m_arguments vector.
350 m_arguments.push_back(arg1);
351 }
352
353 ~CommandObjectLogDump() override = default;
354
GetOptions()355 Options *GetOptions() override { return &m_options; }
356
357 class CommandOptions : public Options {
358 public:
359 CommandOptions() = default;
360
361 ~CommandOptions() override = default;
362
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)363 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
364 ExecutionContext *execution_context) override {
365 Status error;
366 const int short_option = m_getopt_table[option_idx].val;
367
368 switch (short_option) {
369 case 'f':
370 log_file.SetFile(option_arg, FileSpec::Style::native);
371 FileSystem::Instance().Resolve(log_file);
372 break;
373 default:
374 llvm_unreachable("Unimplemented option");
375 }
376
377 return error;
378 }
379
OptionParsingStarting(ExecutionContext * execution_context)380 void OptionParsingStarting(ExecutionContext *execution_context) override {
381 log_file.Clear();
382 }
383
GetDefinitions()384 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
385 return llvm::ArrayRef(g_log_dump_options);
386 }
387
388 FileSpec log_file;
389 };
390
391 void
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)392 HandleArgumentCompletion(CompletionRequest &request,
393 OptionElementVector &opt_element_vector) override {
394 CompleteEnableDisable(request);
395 }
396
397 protected:
DoExecute(Args & args,CommandReturnObject & result)398 void DoExecute(Args &args, CommandReturnObject &result) override {
399 if (args.empty()) {
400 result.AppendErrorWithFormat(
401 "%s takes a log channel and one or more log types.\n",
402 m_cmd_name.c_str());
403 return;
404 }
405
406 std::unique_ptr<llvm::raw_ostream> stream_up;
407 if (m_options.log_file) {
408 const File::OpenOptions flags = File::eOpenOptionWriteOnly |
409 File::eOpenOptionCanCreate |
410 File::eOpenOptionTruncate;
411 llvm::Expected<FileUP> file = FileSystem::Instance().Open(
412 m_options.log_file, flags, lldb::eFilePermissionsFileDefault, false);
413 if (!file) {
414 result.AppendErrorWithFormat("Unable to open log file '%s': %s",
415 m_options.log_file.GetPath().c_str(),
416 llvm::toString(file.takeError()).c_str());
417 return;
418 }
419 stream_up = std::make_unique<llvm::raw_fd_ostream>(
420 (*file)->GetDescriptor(), /*shouldClose=*/true);
421 } else {
422 stream_up = std::make_unique<llvm::raw_fd_ostream>(
423 GetDebugger().GetOutputFile().GetDescriptor(), /*shouldClose=*/false);
424 }
425
426 const std::string channel = std::string(args[0].ref());
427 std::string error;
428 llvm::raw_string_ostream error_stream(error);
429 if (Log::DumpLogChannel(channel, *stream_up, error_stream)) {
430 result.SetStatus(eReturnStatusSuccessFinishNoResult);
431 } else {
432 result.SetStatus(eReturnStatusFailed);
433 result.GetErrorStream() << error_stream.str();
434 }
435 }
436
437 CommandOptions m_options;
438 };
439
440 class CommandObjectLogTimerEnable : public CommandObjectParsed {
441 public:
442 // Constructors and Destructors
CommandObjectLogTimerEnable(CommandInterpreter & interpreter)443 CommandObjectLogTimerEnable(CommandInterpreter &interpreter)
444 : CommandObjectParsed(interpreter, "log timers enable",
445 "enable LLDB internal performance timers",
446 "log timers enable <depth>") {
447 CommandArgumentEntry arg;
448 CommandArgumentData depth_arg;
449
450 // Define the first (and only) variant of this arg.
451 depth_arg.arg_type = eArgTypeCount;
452 depth_arg.arg_repetition = eArgRepeatOptional;
453
454 // There is only one variant this argument could be; put it into the
455 // argument entry.
456 arg.push_back(depth_arg);
457
458 // Push the data for the first argument into the m_arguments vector.
459 m_arguments.push_back(arg);
460 }
461
462 ~CommandObjectLogTimerEnable() override = default;
463
464 protected:
DoExecute(Args & args,CommandReturnObject & result)465 void DoExecute(Args &args, CommandReturnObject &result) override {
466 result.SetStatus(eReturnStatusFailed);
467
468 if (args.GetArgumentCount() == 0) {
469 Timer::SetDisplayDepth(UINT32_MAX);
470 result.SetStatus(eReturnStatusSuccessFinishNoResult);
471 } else if (args.GetArgumentCount() == 1) {
472 uint32_t depth;
473 if (args[0].ref().consumeInteger(0, depth)) {
474 result.AppendError(
475 "Could not convert enable depth to an unsigned integer.");
476 } else {
477 Timer::SetDisplayDepth(depth);
478 result.SetStatus(eReturnStatusSuccessFinishNoResult);
479 }
480 }
481
482 if (!result.Succeeded()) {
483 result.AppendError("Missing subcommand");
484 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
485 }
486 }
487 };
488
489 class CommandObjectLogTimerDisable : public CommandObjectParsed {
490 public:
491 // Constructors and Destructors
CommandObjectLogTimerDisable(CommandInterpreter & interpreter)492 CommandObjectLogTimerDisable(CommandInterpreter &interpreter)
493 : CommandObjectParsed(interpreter, "log timers disable",
494 "disable LLDB internal performance timers",
495 nullptr) {}
496
497 ~CommandObjectLogTimerDisable() override = default;
498
499 protected:
DoExecute(Args & args,CommandReturnObject & result)500 void DoExecute(Args &args, CommandReturnObject &result) override {
501 Timer::DumpCategoryTimes(result.GetOutputStream());
502 Timer::SetDisplayDepth(0);
503 result.SetStatus(eReturnStatusSuccessFinishResult);
504
505 if (!result.Succeeded()) {
506 result.AppendError("Missing subcommand");
507 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
508 }
509 }
510 };
511
512 class CommandObjectLogTimerDump : public CommandObjectParsed {
513 public:
514 // Constructors and Destructors
CommandObjectLogTimerDump(CommandInterpreter & interpreter)515 CommandObjectLogTimerDump(CommandInterpreter &interpreter)
516 : CommandObjectParsed(interpreter, "log timers dump",
517 "dump LLDB internal performance timers", nullptr) {}
518
519 ~CommandObjectLogTimerDump() override = default;
520
521 protected:
DoExecute(Args & args,CommandReturnObject & result)522 void DoExecute(Args &args, CommandReturnObject &result) override {
523 Timer::DumpCategoryTimes(result.GetOutputStream());
524 result.SetStatus(eReturnStatusSuccessFinishResult);
525
526 if (!result.Succeeded()) {
527 result.AppendError("Missing subcommand");
528 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
529 }
530 }
531 };
532
533 class CommandObjectLogTimerReset : public CommandObjectParsed {
534 public:
535 // Constructors and Destructors
CommandObjectLogTimerReset(CommandInterpreter & interpreter)536 CommandObjectLogTimerReset(CommandInterpreter &interpreter)
537 : CommandObjectParsed(interpreter, "log timers reset",
538 "reset LLDB internal performance timers", nullptr) {
539 }
540
541 ~CommandObjectLogTimerReset() override = default;
542
543 protected:
DoExecute(Args & args,CommandReturnObject & result)544 void DoExecute(Args &args, CommandReturnObject &result) override {
545 Timer::ResetCategoryTimes();
546 result.SetStatus(eReturnStatusSuccessFinishResult);
547
548 if (!result.Succeeded()) {
549 result.AppendError("Missing subcommand");
550 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
551 }
552 }
553 };
554
555 class CommandObjectLogTimerIncrement : public CommandObjectParsed {
556 public:
557 // Constructors and Destructors
CommandObjectLogTimerIncrement(CommandInterpreter & interpreter)558 CommandObjectLogTimerIncrement(CommandInterpreter &interpreter)
559 : CommandObjectParsed(interpreter, "log timers increment",
560 "increment LLDB internal performance timers",
561 "log timers increment <bool>") {
562 CommandArgumentEntry arg;
563 CommandArgumentData bool_arg;
564
565 // Define the first (and only) variant of this arg.
566 bool_arg.arg_type = eArgTypeBoolean;
567 bool_arg.arg_repetition = eArgRepeatPlain;
568
569 // There is only one variant this argument could be; put it into the
570 // argument entry.
571 arg.push_back(bool_arg);
572
573 // Push the data for the first argument into the m_arguments vector.
574 m_arguments.push_back(arg);
575 }
576
577 ~CommandObjectLogTimerIncrement() override = default;
578
579 void
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)580 HandleArgumentCompletion(CompletionRequest &request,
581 OptionElementVector &opt_element_vector) override {
582 request.TryCompleteCurrentArg("true");
583 request.TryCompleteCurrentArg("false");
584 }
585
586 protected:
DoExecute(Args & args,CommandReturnObject & result)587 void DoExecute(Args &args, CommandReturnObject &result) override {
588 result.SetStatus(eReturnStatusFailed);
589
590 if (args.GetArgumentCount() == 1) {
591 bool success;
592 bool increment =
593 OptionArgParser::ToBoolean(args[0].ref(), false, &success);
594
595 if (success) {
596 Timer::SetQuiet(!increment);
597 result.SetStatus(eReturnStatusSuccessFinishNoResult);
598 } else
599 result.AppendError("Could not convert increment value to boolean.");
600 }
601
602 if (!result.Succeeded()) {
603 result.AppendError("Missing subcommand");
604 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
605 }
606 }
607 };
608
609 class CommandObjectLogTimer : public CommandObjectMultiword {
610 public:
CommandObjectLogTimer(CommandInterpreter & interpreter)611 CommandObjectLogTimer(CommandInterpreter &interpreter)
612 : CommandObjectMultiword(interpreter, "log timers",
613 "Enable, disable, dump, and reset LLDB internal "
614 "performance timers.",
615 "log timers < enable <depth> | disable | dump | "
616 "increment <bool> | reset >") {
617 LoadSubCommand("enable", CommandObjectSP(
618 new CommandObjectLogTimerEnable(interpreter)));
619 LoadSubCommand("disable", CommandObjectSP(new CommandObjectLogTimerDisable(
620 interpreter)));
621 LoadSubCommand("dump",
622 CommandObjectSP(new CommandObjectLogTimerDump(interpreter)));
623 LoadSubCommand(
624 "reset", CommandObjectSP(new CommandObjectLogTimerReset(interpreter)));
625 LoadSubCommand(
626 "increment",
627 CommandObjectSP(new CommandObjectLogTimerIncrement(interpreter)));
628 }
629
630 ~CommandObjectLogTimer() override = default;
631 };
632
CommandObjectLog(CommandInterpreter & interpreter)633 CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter)
634 : CommandObjectMultiword(interpreter, "log",
635 "Commands controlling LLDB internal logging.",
636 "log <subcommand> [<command-options>]") {
637 LoadSubCommand("enable",
638 CommandObjectSP(new CommandObjectLogEnable(interpreter)));
639 LoadSubCommand("disable",
640 CommandObjectSP(new CommandObjectLogDisable(interpreter)));
641 LoadSubCommand("list",
642 CommandObjectSP(new CommandObjectLogList(interpreter)));
643 LoadSubCommand("dump",
644 CommandObjectSP(new CommandObjectLogDump(interpreter)));
645 LoadSubCommand("timers",
646 CommandObjectSP(new CommandObjectLogTimer(interpreter)));
647 }
648
649 CommandObjectLog::~CommandObjectLog() = default;
650