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 return Status::FromErrorStringWithFormatv(
103 "unrecognized value for log handler '{0}'", option_arg
);
106 return buffer_size
.SetValueFromString(option_arg
,
107 eVarSetOperationAssign
);
109 log_options
|= LLDB_LOG_OPTION_VERBOSE
;
112 log_options
|= LLDB_LOG_OPTION_PREPEND_SEQUENCE
;
115 log_options
|= LLDB_LOG_OPTION_PREPEND_TIMESTAMP
;
118 log_options
|= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD
;
121 log_options
|= LLDB_LOG_OPTION_PREPEND_THREAD_NAME
;
124 log_options
|= LLDB_LOG_OPTION_BACKTRACE
;
127 log_options
|= LLDB_LOG_OPTION_APPEND
;
130 log_options
|= LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION
;
133 llvm_unreachable("Unimplemented option");
139 void OptionParsingStarting(ExecutionContext
*execution_context
) override
{
142 handler
= eLogHandlerStream
;
146 llvm::ArrayRef
<OptionDefinition
> GetDefinitions() override
{
147 return llvm::ArrayRef(g_log_enable_options
);
151 OptionValueUInt64 buffer_size
;
152 LogHandlerKind handler
= eLogHandlerStream
;
153 uint32_t log_options
= 0;
157 HandleArgumentCompletion(CompletionRequest
&request
,
158 OptionElementVector
&opt_element_vector
) override
{
159 CompleteEnableDisable(request
);
163 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
164 if (args
.GetArgumentCount() < 2) {
165 result
.AppendErrorWithFormat(
166 "%s takes a log channel and one or more log types.\n",
171 if (m_options
.handler
== eLogHandlerCircular
&&
172 m_options
.buffer_size
.GetCurrentValue() == 0) {
174 "the circular buffer handler requires a non-zero buffer size.\n");
178 if ((m_options
.handler
!= eLogHandlerCircular
&&
179 m_options
.handler
!= eLogHandlerStream
) &&
180 m_options
.buffer_size
.GetCurrentValue() != 0) {
181 result
.AppendError("a buffer size can only be specified for the circular "
182 "and stream buffer handler.\n");
186 if (m_options
.handler
!= eLogHandlerStream
&& m_options
.log_file
) {
188 "a file name can only be specified for the stream handler.\n");
192 // Store into a std::string since we're about to shift the channel off.
193 const std::string channel
= std::string(args
[0].ref());
194 args
.Shift(); // Shift off the channel
195 char log_file
[PATH_MAX
];
196 if (m_options
.log_file
)
197 m_options
.log_file
.GetPath(log_file
, sizeof(log_file
));
202 llvm::raw_string_ostream
error_stream(error
);
203 bool success
= GetDebugger().EnableLog(
204 channel
, args
.GetArgumentArrayRef(), log_file
, m_options
.log_options
,
205 m_options
.buffer_size
.GetCurrentValue(), m_options
.handler
,
207 result
.GetErrorStream() << error
;
210 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
212 result
.SetStatus(eReturnStatusFailed
);
215 CommandOptions m_options
;
218 class CommandObjectLogDisable
: public CommandObjectParsed
{
220 // Constructors and Destructors
221 CommandObjectLogDisable(CommandInterpreter
&interpreter
)
222 : CommandObjectParsed(interpreter
, "log disable",
223 "Disable one or more log channel categories.",
225 CommandArgumentEntry arg1
;
226 CommandArgumentEntry arg2
;
227 CommandArgumentData channel_arg
;
228 CommandArgumentData category_arg
;
230 // Define the first (and only) variant of this arg.
231 channel_arg
.arg_type
= eArgTypeLogChannel
;
232 channel_arg
.arg_repetition
= eArgRepeatPlain
;
234 // There is only one variant this argument could be; put it into the
236 arg1
.push_back(channel_arg
);
238 category_arg
.arg_type
= eArgTypeLogCategory
;
239 category_arg
.arg_repetition
= eArgRepeatPlus
;
241 arg2
.push_back(category_arg
);
243 // Push the data for the first argument into the m_arguments vector.
244 m_arguments
.push_back(arg1
);
245 m_arguments
.push_back(arg2
);
248 ~CommandObjectLogDisable() override
= default;
251 HandleArgumentCompletion(CompletionRequest
&request
,
252 OptionElementVector
&opt_element_vector
) override
{
253 CompleteEnableDisable(request
);
257 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
259 result
.AppendErrorWithFormat(
260 "%s takes a log channel and one or more log types.\n",
265 const std::string channel
= std::string(args
[0].ref());
266 args
.Shift(); // Shift off the channel
267 if (channel
== "all") {
268 Log::DisableAllLogChannels();
269 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
272 llvm::raw_string_ostream
error_stream(error
);
273 if (Log::DisableLogChannel(channel
, args
.GetArgumentArrayRef(),
275 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
276 result
.GetErrorStream() << error
;
281 class CommandObjectLogList
: public CommandObjectParsed
{
283 // Constructors and Destructors
284 CommandObjectLogList(CommandInterpreter
&interpreter
)
285 : CommandObjectParsed(interpreter
, "log list",
286 "List the log categories for one or more log "
287 "channels. If none specified, lists them all.",
289 AddSimpleArgumentList(eArgTypeLogChannel
, eArgRepeatStar
);
292 ~CommandObjectLogList() override
= default;
295 HandleArgumentCompletion(CompletionRequest
&request
,
296 OptionElementVector
&opt_element_vector
) override
{
297 for (llvm::StringRef channel
: Log::ListChannels())
298 request
.TryCompleteCurrentArg(channel
);
302 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
304 llvm::raw_string_ostream
output_stream(output
);
306 Log::ListAllLogChannels(output_stream
);
307 result
.SetStatus(eReturnStatusSuccessFinishResult
);
310 for (const auto &entry
: args
.entries())
312 success
&& Log::ListChannelCategories(entry
.ref(), output_stream
);
314 result
.SetStatus(eReturnStatusSuccessFinishResult
);
316 result
.GetOutputStream() << output
;
319 class CommandObjectLogDump
: public CommandObjectParsed
{
321 CommandObjectLogDump(CommandInterpreter
&interpreter
)
322 : CommandObjectParsed(interpreter
, "log dump",
323 "dump circular buffer logs", nullptr) {
324 AddSimpleArgumentList(eArgTypeLogChannel
);
327 ~CommandObjectLogDump() override
= default;
329 Options
*GetOptions() override
{ return &m_options
; }
331 class CommandOptions
: public Options
{
333 CommandOptions() = default;
335 ~CommandOptions() override
= default;
337 Status
SetOptionValue(uint32_t option_idx
, llvm::StringRef option_arg
,
338 ExecutionContext
*execution_context
) override
{
340 const int short_option
= m_getopt_table
[option_idx
].val
;
342 switch (short_option
) {
344 log_file
.SetFile(option_arg
, FileSpec::Style::native
);
345 FileSystem::Instance().Resolve(log_file
);
348 llvm_unreachable("Unimplemented option");
354 void OptionParsingStarting(ExecutionContext
*execution_context
) override
{
358 llvm::ArrayRef
<OptionDefinition
> GetDefinitions() override
{
359 return llvm::ArrayRef(g_log_dump_options
);
366 HandleArgumentCompletion(CompletionRequest
&request
,
367 OptionElementVector
&opt_element_vector
) override
{
368 CompleteEnableDisable(request
);
372 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
374 result
.AppendErrorWithFormat(
375 "%s takes a log channel and one or more log types.\n",
380 std::unique_ptr
<llvm::raw_ostream
> stream_up
;
381 if (m_options
.log_file
) {
382 const File::OpenOptions flags
= File::eOpenOptionWriteOnly
|
383 File::eOpenOptionCanCreate
|
384 File::eOpenOptionTruncate
;
385 llvm::Expected
<FileUP
> file
= FileSystem::Instance().Open(
386 m_options
.log_file
, flags
, lldb::eFilePermissionsFileDefault
, false);
388 result
.AppendErrorWithFormat("Unable to open log file '%s': %s",
389 m_options
.log_file
.GetPath().c_str(),
390 llvm::toString(file
.takeError()).c_str());
393 stream_up
= std::make_unique
<llvm::raw_fd_ostream
>(
394 (*file
)->GetDescriptor(), /*shouldClose=*/true);
396 stream_up
= std::make_unique
<llvm::raw_fd_ostream
>(
397 GetDebugger().GetOutputFile().GetDescriptor(), /*shouldClose=*/false);
400 const std::string channel
= std::string(args
[0].ref());
402 llvm::raw_string_ostream
error_stream(error
);
403 if (Log::DumpLogChannel(channel
, *stream_up
, error_stream
)) {
404 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
406 result
.SetStatus(eReturnStatusFailed
);
407 result
.GetErrorStream() << error
;
411 CommandOptions m_options
;
414 class CommandObjectLogTimerEnable
: public CommandObjectParsed
{
416 // Constructors and Destructors
417 CommandObjectLogTimerEnable(CommandInterpreter
&interpreter
)
418 : CommandObjectParsed(interpreter
, "log timers enable",
419 "enable LLDB internal performance timers",
420 "log timers enable <depth>") {
421 AddSimpleArgumentList(eArgTypeCount
, eArgRepeatOptional
);
424 ~CommandObjectLogTimerEnable() override
= default;
427 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
428 result
.SetStatus(eReturnStatusFailed
);
430 if (args
.GetArgumentCount() == 0) {
431 Timer::SetDisplayDepth(UINT32_MAX
);
432 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
433 } else if (args
.GetArgumentCount() == 1) {
435 if (args
[0].ref().consumeInteger(0, depth
)) {
437 "Could not convert enable depth to an unsigned integer.");
439 Timer::SetDisplayDepth(depth
);
440 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
444 if (!result
.Succeeded()) {
445 result
.AppendError("Missing subcommand");
446 result
.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax
.c_str());
451 class CommandObjectLogTimerDisable
: public CommandObjectParsed
{
453 // Constructors and Destructors
454 CommandObjectLogTimerDisable(CommandInterpreter
&interpreter
)
455 : CommandObjectParsed(interpreter
, "log timers disable",
456 "disable LLDB internal performance timers",
459 ~CommandObjectLogTimerDisable() override
= default;
462 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
463 Timer::DumpCategoryTimes(result
.GetOutputStream());
464 Timer::SetDisplayDepth(0);
465 result
.SetStatus(eReturnStatusSuccessFinishResult
);
467 if (!result
.Succeeded()) {
468 result
.AppendError("Missing subcommand");
469 result
.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax
.c_str());
474 class CommandObjectLogTimerDump
: public CommandObjectParsed
{
476 // Constructors and Destructors
477 CommandObjectLogTimerDump(CommandInterpreter
&interpreter
)
478 : CommandObjectParsed(interpreter
, "log timers dump",
479 "dump LLDB internal performance timers", nullptr) {}
481 ~CommandObjectLogTimerDump() override
= default;
484 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
485 Timer::DumpCategoryTimes(result
.GetOutputStream());
486 result
.SetStatus(eReturnStatusSuccessFinishResult
);
488 if (!result
.Succeeded()) {
489 result
.AppendError("Missing subcommand");
490 result
.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax
.c_str());
495 class CommandObjectLogTimerReset
: public CommandObjectParsed
{
497 // Constructors and Destructors
498 CommandObjectLogTimerReset(CommandInterpreter
&interpreter
)
499 : CommandObjectParsed(interpreter
, "log timers reset",
500 "reset LLDB internal performance timers", nullptr) {
503 ~CommandObjectLogTimerReset() override
= default;
506 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
507 Timer::ResetCategoryTimes();
508 result
.SetStatus(eReturnStatusSuccessFinishResult
);
510 if (!result
.Succeeded()) {
511 result
.AppendError("Missing subcommand");
512 result
.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax
.c_str());
517 class CommandObjectLogTimerIncrement
: public CommandObjectParsed
{
519 // Constructors and Destructors
520 CommandObjectLogTimerIncrement(CommandInterpreter
&interpreter
)
521 : CommandObjectParsed(interpreter
, "log timers increment",
522 "increment LLDB internal performance timers",
523 "log timers increment <bool>") {
524 AddSimpleArgumentList(eArgTypeBoolean
);
527 ~CommandObjectLogTimerIncrement() override
= default;
530 HandleArgumentCompletion(CompletionRequest
&request
,
531 OptionElementVector
&opt_element_vector
) override
{
532 request
.TryCompleteCurrentArg("true");
533 request
.TryCompleteCurrentArg("false");
537 void DoExecute(Args
&args
, CommandReturnObject
&result
) override
{
538 result
.SetStatus(eReturnStatusFailed
);
540 if (args
.GetArgumentCount() == 1) {
543 OptionArgParser::ToBoolean(args
[0].ref(), false, &success
);
546 Timer::SetQuiet(!increment
);
547 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
549 result
.AppendError("Could not convert increment value to boolean.");
552 if (!result
.Succeeded()) {
553 result
.AppendError("Missing subcommand");
554 result
.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax
.c_str());
559 class CommandObjectLogTimer
: public CommandObjectMultiword
{
561 CommandObjectLogTimer(CommandInterpreter
&interpreter
)
562 : CommandObjectMultiword(interpreter
, "log timers",
563 "Enable, disable, dump, and reset LLDB internal "
564 "performance timers.",
565 "log timers < enable <depth> | disable | dump | "
566 "increment <bool> | reset >") {
567 LoadSubCommand("enable", CommandObjectSP(
568 new CommandObjectLogTimerEnable(interpreter
)));
569 LoadSubCommand("disable", CommandObjectSP(new CommandObjectLogTimerDisable(
571 LoadSubCommand("dump",
572 CommandObjectSP(new CommandObjectLogTimerDump(interpreter
)));
574 "reset", CommandObjectSP(new CommandObjectLogTimerReset(interpreter
)));
577 CommandObjectSP(new CommandObjectLogTimerIncrement(interpreter
)));
580 ~CommandObjectLogTimer() override
= default;
583 CommandObjectLog::CommandObjectLog(CommandInterpreter
&interpreter
)
584 : CommandObjectMultiword(interpreter
, "log",
585 "Commands controlling LLDB internal logging.",
586 "log <subcommand> [<command-options>]") {
587 LoadSubCommand("enable",
588 CommandObjectSP(new CommandObjectLogEnable(interpreter
)));
589 LoadSubCommand("disable",
590 CommandObjectSP(new CommandObjectLogDisable(interpreter
)));
591 LoadSubCommand("list",
592 CommandObjectSP(new CommandObjectLogList(interpreter
)));
593 LoadSubCommand("dump",
594 CommandObjectSP(new CommandObjectLogDump(interpreter
)));
595 LoadSubCommand("timers",
596 CommandObjectSP(new CommandObjectLogTimer(interpreter
)));
599 CommandObjectLog::~CommandObjectLog() = default;