Index: lldb/include/lldb/Core/Debugger.h =================================================================== --- lldb/include/lldb/Core/Debugger.h +++ lldb/include/lldb/Core/Debugger.h @@ -245,7 +245,8 @@ bool EnableLog(llvm::StringRef channel, llvm::ArrayRef categories, llvm::StringRef log_file, uint32_t log_options, - size_t buffer_size, llvm::raw_ostream &error_stream); + size_t buffer_size, LogHandlerKind log_handler_kind, + llvm::raw_ostream &error_stream); void SetLoggingCallback(lldb::LogOutputCallback log_callback, void *baton); Index: lldb/include/lldb/lldb-enumerations.h =================================================================== --- lldb/include/lldb/lldb-enumerations.h +++ lldb/include/lldb/lldb-enumerations.h @@ -602,6 +602,7 @@ eArgTypeColumnNum, eArgTypeModuleUUID, eArgTypeSaveCoreStyle, + eArgTypeLogHandler, eArgTypeLastArg // Always keep this entry as the last entry in this // enumeration!! }; Index: lldb/include/lldb/lldb-private-enumerations.h =================================================================== --- lldb/include/lldb/lldb-private-enumerations.h +++ lldb/include/lldb/lldb-private-enumerations.h @@ -222,6 +222,14 @@ StatisticMax = 4 }; +// Enumeration that can be used to specify a log handler. +enum LogHandlerKind { + eLogHandlerStream, + eLogHandlerCallback, + eLogHandlerCircular, + eLogHandlerSystem, + eLogHandlerDefault = eLogHandlerStream, +}; inline std::string GetStatDescription(lldb_private::StatisticKind K) { switch (K) { Index: lldb/source/API/SBDebugger.cpp =================================================================== --- lldb/source/API/SBDebugger.cpp +++ lldb/source/API/SBDebugger.cpp @@ -1621,7 +1621,8 @@ std::string error; llvm::raw_string_ostream error_stream(error); return m_opaque_sp->EnableLog(channel, GetCategoryArray(categories), "", - log_options, /*buffer_size=*/0, error_stream); + log_options, /*buffer_size=*/0, + eLogHandlerStream, error_stream); } else return false; } Index: lldb/source/Commands/CommandObjectLog.cpp =================================================================== --- lldb/source/Commands/CommandObjectLog.cpp +++ lldb/source/Commands/CommandObjectLog.cpp @@ -11,6 +11,7 @@ #include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionValueEnumeration.h" #include "lldb/Interpreter/OptionValueUInt64.h" #include "lldb/Interpreter/Options.h" #include "lldb/Utility/Args.h" @@ -22,6 +23,36 @@ using namespace lldb; using namespace lldb_private; +static constexpr OptionEnumValueElement g_log_handler_type[] = { + { + eLogHandlerDefault, + "default", + "Use the default (stream) log handler", + }, + { + eLogHandlerStream, + "stream", + "Write log messages to the debugger output stream or to a file if one " + "is specified. A buffer size (in bytes) can be specified with -b. If " + "no buffer size is specified the output is unbuffered.", + }, + { + eLogHandlerCircular, + "circular", + "Write log messages to a fixed size circular buffer. A buffer size " + "(number of messages) must be specified with -b.", + }, + { + eLogHandlerSystem, + "os", + "Write log messages to the operating system log.", + }, +}; + +static constexpr OptionEnumValues LogHandlerType() { + return OptionEnumValues(g_log_handler_type); +} + #define LLDB_OPTIONS_log_enable #include "CommandOptions.inc" @@ -90,6 +121,14 @@ log_file.SetFile(option_arg, FileSpec::Style::native); FileSystem::Instance().Resolve(log_file); break; + case 'h': + handler = (LogHandlerKind)OptionArgParser::ToOptionEnum( + option_arg, GetDefinitions()[option_idx].enum_values, 0, error); + if (!error.Success()) + error.SetErrorStringWithFormat( + "unrecognized value for log handler '%s'", + option_arg.str().c_str()); + break; case 'b': error = buffer_size.SetValueFromString(option_arg, eVarSetOperationAssign); @@ -128,6 +167,7 @@ void OptionParsingStarting(ExecutionContext *execution_context) override { log_file.Clear(); buffer_size.Clear(); + handler = eLogHandlerStream; log_options = 0; } @@ -137,6 +177,7 @@ FileSpec log_file; OptionValueUInt64 buffer_size; + LogHandlerKind handler = eLogHandlerStream; uint32_t log_options = 0; }; @@ -155,6 +196,13 @@ return false; } + if (m_options.handler == eLogHandlerCircular && + m_options.buffer_size.GetCurrentValue() == 0) { + result.AppendError( + "the circular buffer handler requires a non-zero buffer size.\n"); + return false; + } + // Store into a std::string since we're about to shift the channel off. const std::string channel = std::string(args[0].ref()); args.Shift(); // Shift off the channel @@ -168,7 +216,8 @@ llvm::raw_string_ostream error_stream(error); bool success = GetDebugger().EnableLog( channel, args.GetArgumentArrayRef(), log_file, m_options.log_options, - m_options.buffer_size.GetCurrentValue(), error_stream); + m_options.buffer_size.GetCurrentValue(), m_options.handler, + error_stream); result.GetErrorStream() << error_stream.str(); if (success) Index: lldb/source/Commands/Options.td =================================================================== --- lldb/source/Commands/Options.td +++ lldb/source/Commands/Options.td @@ -431,8 +431,10 @@ let Command = "log enable" in { def log_file : Option<"file", "f">, Group<1>, Arg<"Filename">, Desc<"Set the destination file to log to.">; + def log_handler : Option<"log-handler", "h">, Group<1>, + EnumArg<"LogHandler", "LogHandlerType()">, Desc<"Specify a log handler which determines where log messages are written.">; def log_buffer_size : Option<"buffer", "b">, Group<1>, Arg<"UnsignedInteger">, - Desc<"Set the log to be buffered, using the specified buffer size.">; + Desc<"Set the log to be buffered, using the specified buffer size, if supported by the log handler.">; def log_verbose : Option<"verbose", "v">, Group<1>, Desc<"Enable verbose logging.">; def log_sequence : Option<"sequence", "s">, Group<1>, Index: lldb/source/Core/Debugger.cpp =================================================================== --- lldb/source/Core/Debugger.cpp +++ lldb/source/Core/Debugger.cpp @@ -1406,11 +1406,27 @@ debugger_id, once); } +static std::shared_ptr +CreateLogHandler(LogHandlerKind log_handler_kind, int fd, bool should_close, + size_t buffer_size) { + switch (log_handler_kind) { + case eLogHandlerStream: + return std::make_shared(fd, should_close, buffer_size); + case eLogHandlerCircular: + return std::make_shared(buffer_size); + case eLogHandlerSystem: + return std::make_shared(); + case eLogHandlerCallback: + return {}; + } + return {}; +} + bool Debugger::EnableLog(llvm::StringRef channel, llvm::ArrayRef categories, llvm::StringRef log_file, uint32_t log_options, - size_t buffer_size, llvm::raw_ostream &error_stream) { - const bool should_close = true; + size_t buffer_size, LogHandlerKind log_handler_kind, + llvm::raw_ostream &error_stream) { std::shared_ptr log_handler_sp; if (m_callback_handler_sp) { @@ -1419,8 +1435,9 @@ log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_THREAD_NAME; } else if (log_file.empty()) { - log_handler_sp = std::make_shared( - GetOutputFile().GetDescriptor(), !should_close, buffer_size); + log_handler_sp = + CreateLogHandler(log_handler_kind, GetOutputFile().GetDescriptor(), + /*should_close=*/false, buffer_size); } else { auto pos = m_stream_handlers.find(log_file); if (pos != m_stream_handlers.end()) @@ -1440,8 +1457,9 @@ return false; } - log_handler_sp = std::make_shared( - (*file)->GetDescriptor(), should_close, buffer_size); + log_handler_sp = + CreateLogHandler(log_handler_kind, (*file)->GetDescriptor(), + /*should_close=*/true, buffer_size); m_stream_handlers[log_file] = log_handler_sp; } } Index: lldb/source/Interpreter/CommandObject.cpp =================================================================== --- lldb/source/Interpreter/CommandObject.cpp +++ lldb/source/Interpreter/CommandObject.cpp @@ -1126,7 +1126,8 @@ { eArgTypeCommand, "command", CommandCompletions::eNoCompletion, { nullptr, false }, "An LLDB Command line command element." }, { eArgTypeColumnNum, "column", CommandCompletions::eNoCompletion, { nullptr, false }, "Column number in a source file." }, { eArgTypeModuleUUID, "module-uuid", CommandCompletions::eModuleUUIDCompletion, { nullptr, false }, "A module UUID value." }, - { eArgTypeSaveCoreStyle, "corefile-style", CommandCompletions::eNoCompletion, { nullptr, false }, "The type of corefile that lldb will try to create, dependant on this target's capabilities." } + { eArgTypeSaveCoreStyle, "corefile-style", CommandCompletions::eNoCompletion, { nullptr, false }, "The type of corefile that lldb will try to create, dependant on this target's capabilities." }, + { eArgTypeLogHandler, "log-handler", CommandCompletions::eNoCompletion, { nullptr, false }, "The log handle that will be used to write out log messages." }, // clang-format on }; Index: lldb/tools/lldb-test/lldb-test.cpp =================================================================== --- lldb/tools/lldb-test/lldb-test.cpp +++ lldb/tools/lldb-test/lldb-test.cpp @@ -1120,7 +1120,7 @@ /*add_to_history*/ eLazyBoolNo, Result); if (!opts::Log.empty()) - Dbg->EnableLog("lldb", {"all"}, opts::Log, 0, 0, errs()); + Dbg->EnableLog("lldb", {"all"}, opts::Log, 0, 0, eLogHandlerStream, errs()); if (opts::BreakpointSubcommand) return opts::breakpoint::evaluateBreakpoints(*Dbg);