1 //===-- CommandObjectLog.cpp ----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
25 using namespace lldb_private
;
27 #define LLDB_OPTIONS_log_enable
28 #include "CommandOptions.inc"
30 #define LLDB_OPTIONS_log_dump
31 #include "CommandOptions.inc"
33 /// Common completion logic for log enable/disable.
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
);
48 class CommandObjectLogEnable
: public CommandObjectParsed
{
50 // Constructors and Destructors
51 CommandObjectLogEnable(CommandInterpreter
&interpreter
)
52 : CommandObjectParsed(interpreter
, "log enable",
53 "Enable logging for a single log channel.",
55 CommandArgumentEntry arg1
;
56 CommandArgumentEntry arg2
;
57 CommandArgumentData channel_arg
;
58 CommandArgumentData category_arg
;
60 // Define the first (and only) variant of this arg.
61 channel_arg
.arg_type
= eArgTypeLogChannel
;
62 channel_arg
.arg_repetition
= eArgRepeatPlain
;
64 // There is only one variant this argument could be; put it into the
66 arg1
.push_back(channel_arg
);
68 category_arg
.arg_type
= eArgTypeLogCategory
;
69 category_arg
.arg_repetition
= eArgRepeatPlus
;
71 arg2
.push_back(category_arg
);
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
);
78 ~CommandObjectLogEnable() override
= default;
80 Options
*GetOptions() override
{ return &m_options
; }
82 class CommandOptions
: public Options
{
84 CommandOptions() = default;
86 ~CommandOptions() override
= default;
88 Status
SetOptionValue(uint32_t option_idx
, llvm::StringRef option_arg
,
89 ExecutionContext
*execution_context
) override
{
91 const int short_option
= m_getopt_table
[option_idx
].val
;
93 switch (short_option
) {
95 log_file
.SetFile(option_arg
, FileSpec::Style::native
);
96 FileSystem::Instance().Resolve(log_file
);
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());
108 buffer_size
.SetValueFromString(option_arg
, eVarSetOperationAssign
);
111 log_options
|= LLDB_LOG_OPTION_VERBOSE
;
114 log_options
|= LLDB_LOG_OPTION_PREPEND_SEQUENCE
;
117 log_options
|= LLDB_LOG_OPTION_PREPEND_TIMESTAMP
;
120 log_options
|= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD
;
123 log_options
|= LLDB_LOG_OPTION_PREPEND_THREAD_NAME
;
126 log_options
|= LLDB_LOG_OPTION_BACKTRACE
;
129 log_options
|= LLDB_LOG_OPTION_APPEND
;
132 log_options
|= LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION
;
135 llvm_unreachable("Unimplemented option");
141 void OptionParsingStarting(ExecutionContext
*execution_context
) override
{
144 handler
= eLogHandlerStream
;
148 llvm::ArrayRef
<OptionDefinition
> GetDefinitions() override
{
149 return llvm::ArrayRef(g_log_enable_options
);
153 OptionValueUInt64 buffer_size
;
154 LogHandlerKind handler
= eLogHandlerStream
;
155 uint32_t log_options
= 0;
159 HandleArgumentCompletion(CompletionRequest
&request
,
160 OptionElementVector
&opt_element_vector
) override
{
161 CompleteEnableDisable(request
);
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",
173 if (m_options
.handler
== eLogHandlerCircular
&&
174 m_options
.buffer_size
.GetCurrentValue() == 0) {
176 "the circular buffer handler requires a non-zero buffer size.\n");
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");
188 if (m_options
.handler
!= eLogHandlerStream
&& m_options
.log_file
) {
190 "a file name can only be specified for the stream handler.\n");
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
));
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
,
209 result
.GetErrorStream() << error_stream
.str();
212 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
214 result
.SetStatus(eReturnStatusFailed
);
217 CommandOptions m_options
;
220 class CommandObjectLogDisable
: public CommandObjectParsed
{
222 // Constructors and Destructors
223 CommandObjectLogDisable(CommandInterpreter
&interpreter
)
224 : CommandObjectParsed(interpreter
, "log disable",
225 "Disable one or more log channel categories.",
227 CommandArgumentEntry arg1
;
228 CommandArgumentEntry arg2
;
229 CommandArgumentData channel_arg
;
230 CommandArgumentData category_arg
;
232 // Define the first (and only) variant of this arg.
233 channel_arg
.arg_type
= eArgTypeLogChannel
;
234 channel_arg
.arg_repetition
= eArgRepeatPlain
;
236 // There is only one variant this argument could be; put it into the
238 arg1
.push_back(channel_arg
);
240 category_arg
.arg_type
= eArgTypeLogCategory
;
241 category_arg
.arg_repetition
= eArgRepeatPlus
;
243 arg2
.push_back(category_arg
);
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
);
250 ~CommandObjectLogDisable() override
= default;
253 HandleArgumentCompletion(CompletionRequest
&request
,
254 OptionElementVector
&opt_element_vector
) override
{
255 CompleteEnableDisable(request
);
259 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
261 result
.AppendErrorWithFormat(
262 "%s takes a log channel and one or more log types.\n",
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
);
274 llvm::raw_string_ostream
error_stream(error
);
275 if (Log::DisableLogChannel(channel
, args
.GetArgumentArrayRef(),
277 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
278 result
.GetErrorStream() << error_stream
.str();
283 class CommandObjectLogList
: public CommandObjectParsed
{
285 // Constructors and Destructors
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.",
291 CommandArgumentEntry arg
;
292 CommandArgumentData channel_arg
;
294 // Define the first (and only) variant of this arg.
295 channel_arg
.arg_type
= eArgTypeLogChannel
;
296 channel_arg
.arg_repetition
= eArgRepeatStar
;
298 // There is only one variant this argument could be; put it into the
300 arg
.push_back(channel_arg
);
302 // Push the data for the first argument into the m_arguments vector.
303 m_arguments
.push_back(arg
);
306 ~CommandObjectLogList() override
= default;
309 HandleArgumentCompletion(CompletionRequest
&request
,
310 OptionElementVector
&opt_element_vector
) override
{
311 for (llvm::StringRef channel
: Log::ListChannels())
312 request
.TryCompleteCurrentArg(channel
);
316 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
318 llvm::raw_string_ostream
output_stream(output
);
320 Log::ListAllLogChannels(output_stream
);
321 result
.SetStatus(eReturnStatusSuccessFinishResult
);
324 for (const auto &entry
: args
.entries())
326 success
&& Log::ListChannelCategories(entry
.ref(), output_stream
);
328 result
.SetStatus(eReturnStatusSuccessFinishResult
);
330 result
.GetOutputStream() << output_stream
.str();
333 class CommandObjectLogDump
: public CommandObjectParsed
{
335 CommandObjectLogDump(CommandInterpreter
&interpreter
)
336 : CommandObjectParsed(interpreter
, "log dump",
337 "dump circular buffer logs", nullptr) {
338 CommandArgumentEntry arg1
;
339 CommandArgumentData channel_arg
;
341 // Define the first (and only) variant of this arg.
342 channel_arg
.arg_type
= eArgTypeLogChannel
;
343 channel_arg
.arg_repetition
= eArgRepeatPlain
;
345 // There is only one variant this argument could be; put it into the
347 arg1
.push_back(channel_arg
);
349 // Push the data for the first argument into the m_arguments vector.
350 m_arguments
.push_back(arg1
);
353 ~CommandObjectLogDump() override
= default;
355 Options
*GetOptions() override
{ return &m_options
; }
357 class CommandOptions
: public Options
{
359 CommandOptions() = default;
361 ~CommandOptions() override
= default;
363 Status
SetOptionValue(uint32_t option_idx
, llvm::StringRef option_arg
,
364 ExecutionContext
*execution_context
) override
{
366 const int short_option
= m_getopt_table
[option_idx
].val
;
368 switch (short_option
) {
370 log_file
.SetFile(option_arg
, FileSpec::Style::native
);
371 FileSystem::Instance().Resolve(log_file
);
374 llvm_unreachable("Unimplemented option");
380 void OptionParsingStarting(ExecutionContext
*execution_context
) override
{
384 llvm::ArrayRef
<OptionDefinition
> GetDefinitions() override
{
385 return llvm::ArrayRef(g_log_dump_options
);
392 HandleArgumentCompletion(CompletionRequest
&request
,
393 OptionElementVector
&opt_element_vector
) override
{
394 CompleteEnableDisable(request
);
398 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
400 result
.AppendErrorWithFormat(
401 "%s takes a log channel and one or more log types.\n",
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);
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());
419 stream_up
= std::make_unique
<llvm::raw_fd_ostream
>(
420 (*file
)->GetDescriptor(), /*shouldClose=*/true);
422 stream_up
= std::make_unique
<llvm::raw_fd_ostream
>(
423 GetDebugger().GetOutputFile().GetDescriptor(), /*shouldClose=*/false);
426 const std::string channel
= std::string(args
[0].ref());
428 llvm::raw_string_ostream
error_stream(error
);
429 if (Log::DumpLogChannel(channel
, *stream_up
, error_stream
)) {
430 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
432 result
.SetStatus(eReturnStatusFailed
);
433 result
.GetErrorStream() << error_stream
.str();
437 CommandOptions m_options
;
440 class CommandObjectLogTimerEnable
: public CommandObjectParsed
{
442 // Constructors and Destructors
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
;
450 // Define the first (and only) variant of this arg.
451 depth_arg
.arg_type
= eArgTypeCount
;
452 depth_arg
.arg_repetition
= eArgRepeatOptional
;
454 // There is only one variant this argument could be; put it into the
456 arg
.push_back(depth_arg
);
458 // Push the data for the first argument into the m_arguments vector.
459 m_arguments
.push_back(arg
);
462 ~CommandObjectLogTimerEnable() override
= default;
465 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
466 result
.SetStatus(eReturnStatusFailed
);
468 if (args
.GetArgumentCount() == 0) {
469 Timer::SetDisplayDepth(UINT32_MAX
);
470 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
471 } else if (args
.GetArgumentCount() == 1) {
473 if (args
[0].ref().consumeInteger(0, depth
)) {
475 "Could not convert enable depth to an unsigned integer.");
477 Timer::SetDisplayDepth(depth
);
478 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
482 if (!result
.Succeeded()) {
483 result
.AppendError("Missing subcommand");
484 result
.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax
.c_str());
489 class CommandObjectLogTimerDisable
: public CommandObjectParsed
{
491 // Constructors and Destructors
492 CommandObjectLogTimerDisable(CommandInterpreter
&interpreter
)
493 : CommandObjectParsed(interpreter
, "log timers disable",
494 "disable LLDB internal performance timers",
497 ~CommandObjectLogTimerDisable() override
= default;
500 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
501 Timer::DumpCategoryTimes(result
.GetOutputStream());
502 Timer::SetDisplayDepth(0);
503 result
.SetStatus(eReturnStatusSuccessFinishResult
);
505 if (!result
.Succeeded()) {
506 result
.AppendError("Missing subcommand");
507 result
.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax
.c_str());
512 class CommandObjectLogTimerDump
: public CommandObjectParsed
{
514 // Constructors and Destructors
515 CommandObjectLogTimerDump(CommandInterpreter
&interpreter
)
516 : CommandObjectParsed(interpreter
, "log timers dump",
517 "dump LLDB internal performance timers", nullptr) {}
519 ~CommandObjectLogTimerDump() override
= default;
522 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
523 Timer::DumpCategoryTimes(result
.GetOutputStream());
524 result
.SetStatus(eReturnStatusSuccessFinishResult
);
526 if (!result
.Succeeded()) {
527 result
.AppendError("Missing subcommand");
528 result
.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax
.c_str());
533 class CommandObjectLogTimerReset
: public CommandObjectParsed
{
535 // Constructors and Destructors
536 CommandObjectLogTimerReset(CommandInterpreter
&interpreter
)
537 : CommandObjectParsed(interpreter
, "log timers reset",
538 "reset LLDB internal performance timers", nullptr) {
541 ~CommandObjectLogTimerReset() override
= default;
544 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
545 Timer::ResetCategoryTimes();
546 result
.SetStatus(eReturnStatusSuccessFinishResult
);
548 if (!result
.Succeeded()) {
549 result
.AppendError("Missing subcommand");
550 result
.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax
.c_str());
555 class CommandObjectLogTimerIncrement
: public CommandObjectParsed
{
557 // Constructors and Destructors
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
;
565 // Define the first (and only) variant of this arg.
566 bool_arg
.arg_type
= eArgTypeBoolean
;
567 bool_arg
.arg_repetition
= eArgRepeatPlain
;
569 // There is only one variant this argument could be; put it into the
571 arg
.push_back(bool_arg
);
573 // Push the data for the first argument into the m_arguments vector.
574 m_arguments
.push_back(arg
);
577 ~CommandObjectLogTimerIncrement() override
= default;
580 HandleArgumentCompletion(CompletionRequest
&request
,
581 OptionElementVector
&opt_element_vector
) override
{
582 request
.TryCompleteCurrentArg("true");
583 request
.TryCompleteCurrentArg("false");
587 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
588 result
.SetStatus(eReturnStatusFailed
);
590 if (args
.GetArgumentCount() == 1) {
593 OptionArgParser::ToBoolean(args
[0].ref(), false, &success
);
596 Timer::SetQuiet(!increment
);
597 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
599 result
.AppendError("Could not convert increment value to boolean.");
602 if (!result
.Succeeded()) {
603 result
.AppendError("Missing subcommand");
604 result
.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax
.c_str());
609 class CommandObjectLogTimer
: public CommandObjectMultiword
{
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(
621 LoadSubCommand("dump",
622 CommandObjectSP(new CommandObjectLogTimerDump(interpreter
)));
624 "reset", CommandObjectSP(new CommandObjectLogTimerReset(interpreter
)));
627 CommandObjectSP(new CommandObjectLogTimerIncrement(interpreter
)));
630 ~CommandObjectLogTimer() override
= default;
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
)));
649 CommandObjectLog::~CommandObjectLog() = default;