[lldb] Add support for specifying a log handler
authorJonas Devlieghere <jonas@devlieghere.com>
Fri, 24 Jun 2022 19:53:10 +0000 (12:53 -0700)
committerJonas Devlieghere <jonas@devlieghere.com>
Sat, 25 Jun 2022 01:24:00 +0000 (18:24 -0700)
This patch adds a new flag to `log enable`, allowing the user to specify
a custom log handler. In addition to the default (stream) handler, this
allows using the circular log handler (which logs to a fixed size,
in-memory circular buffer) as well as the system log handler (which logs
to the operating system log).

Differential revision: https://reviews.llvm.org/D128323

lldb/include/lldb/Core/Debugger.h
lldb/include/lldb/lldb-enumerations.h
lldb/include/lldb/lldb-private-enumerations.h
lldb/source/API/SBDebugger.cpp
lldb/source/Commands/CommandObjectLog.cpp
lldb/source/Commands/Options.td
lldb/source/Core/Debugger.cpp
lldb/source/Interpreter/CommandObject.cpp
lldb/tools/lldb-test/lldb-test.cpp

index b5e6c30..031c9a9 100644 (file)
@@ -245,7 +245,8 @@ public:
   bool EnableLog(llvm::StringRef channel,
                  llvm::ArrayRef<const char *> 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 41dbeeb..74a82f6 100644 (file)
@@ -602,6 +602,7 @@ enum CommandArgumentType {
   eArgTypeColumnNum,
   eArgTypeModuleUUID,
   eArgTypeSaveCoreStyle,
+  eArgTypeLogHandler,
   eArgTypeLastArg // Always keep this entry as the last entry in this
                   // enumeration!!
 };
index 9bbb889..2d13e6e 100644 (file)
@@ -222,6 +222,14 @@ enum StatisticKind {
   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 8aa2c74..0de934b 100644 (file)
@@ -1621,7 +1621,8 @@ bool SBDebugger::EnableLog(const char *channel, const char **categories) {
     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 91277e3..349af26 100644 (file)
@@ -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"
 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 @@ public:
         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 @@ public:
     void OptionParsingStarting(ExecutionContext *execution_context) override {
       log_file.Clear();
       buffer_size.Clear();
+      handler = eLogHandlerStream;
       log_options = 0;
     }
 
@@ -137,6 +177,7 @@ public:
 
     FileSpec log_file;
     OptionValueUInt64 buffer_size;
+    LogHandlerKind handler = eLogHandlerStream;
     uint32_t log_options = 0;
   };
 
@@ -155,6 +196,13 @@ protected:
       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 @@ protected:
     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 b95fc6b..53e09cf 100644 (file)
@@ -431,8 +431,10 @@ let Command = "history" in {
 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 fd9679c..f17cd88 100644 (file)
@@ -1406,11 +1406,27 @@ void Debugger::ReportError(std::string message,
                        debugger_id, once);
 }
 
+static std::shared_ptr<LogHandler>
+CreateLogHandler(LogHandlerKind log_handler_kind, int fd, bool should_close,
+                 size_t buffer_size) {
+  switch (log_handler_kind) {
+  case eLogHandlerStream:
+    return std::make_shared<StreamLogHandler>(fd, should_close, buffer_size);
+  case eLogHandlerCircular:
+    return std::make_shared<RotatingLogHandler>(buffer_size);
+  case eLogHandlerSystem:
+    return std::make_shared<SystemLogHandler>();
+  case eLogHandlerCallback:
+    return {};
+  }
+  return {};
+}
+
 bool Debugger::EnableLog(llvm::StringRef channel,
                          llvm::ArrayRef<const char *> 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<LogHandler> log_handler_sp;
   if (m_callback_handler_sp) {
@@ -1419,8 +1435,9 @@ bool Debugger::EnableLog(llvm::StringRef channel,
     log_options |=
         LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_THREAD_NAME;
   } else if (log_file.empty()) {
-    log_handler_sp = std::make_shared<StreamLogHandler>(
-        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 @@ bool Debugger::EnableLog(llvm::StringRef channel,
         return false;
       }
 
-      log_handler_sp = std::make_shared<StreamLogHandler>(
-          (*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 b38bf86..824c34c 100644 (file)
@@ -1126,7 +1126,8 @@ CommandObject::ArgumentTableEntry CommandObject::g_arguments_data[] = {
     { 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 ce2b954..bc3d536 100644 (file)
@@ -1120,7 +1120,7 @@ int main(int argc, const char *argv[]) {
       /*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);