JDevlieghere updated this revision to Diff 437786.
JDevlieghere added a comment.

Generalize support for buffering. You can see it in action below:

  (lldb) log enable -b 10 lldb default
  (lldb) target create ./bin/count
  Current executable set to '/Users/jonas/llvm/build-ra/bin/count' (arm64).
  (lldb) b main
   Processing command: target create ./bin/count
   HandleCommand, cmd_obj : 'target create'
   HandleCommand, (revised) command_string: 'target create ./bin/count'
   HandleCommand, wants_raw_input:'False'
   HandleCommand, command line after removing command name(s): './bin/count'
   Target::Target created with architecture arm64 (arm64-apple-macosx12.4.0)
   HandleCommand, command succeeded
   Processing command: b main
   HandleCommand, cmd_obj : '_regexp-break'
   HandleCommand, (revised) command_string: '_regexp-break main'
   HandleCommand, wants_raw_input:'True'
   HandleCommand, command line after removing command name(s): 'main'
   Processing command: breakpoint set --name 'main'
   ...


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D127986/new/

https://reviews.llvm.org/D127986

Files:
  lldb/include/lldb/Core/Debugger.h
  lldb/include/lldb/Utility/Log.h
  lldb/source/API/SBDebugger.cpp
  lldb/source/Commands/CommandObjectLog.cpp
  lldb/source/Commands/Options.td
  lldb/source/Core/Debugger.cpp
  lldb/source/Utility/Log.cpp
  lldb/tools/lldb-test/lldb-test.cpp

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, errs());
+    Dbg->EnableLog("lldb", {"all"}, opts::Log, 0, 0, errs());
 
   if (opts::BreakpointSubcommand)
     return opts::breakpoint::evaluateBreakpoints(*Dbg);
Index: lldb/source/Utility/Log.cpp
===================================================================
--- lldb/source/Utility/Log.cpp
+++ lldb/source/Utility/Log.cpp
@@ -13,6 +13,7 @@
 #include "llvm/ADT/Twine.h"
 #include "llvm/ADT/iterator.h"
 
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/Chrono.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/Path.h"
@@ -106,6 +107,13 @@
   }
 }
 
+void Log::Dump(llvm::raw_ostream &output_stream) {
+  llvm::sys::ScopedReader lock(m_mutex);
+  if (RotatingLogHandler *handler =
+          llvm::dyn_cast<RotatingLogHandler>(m_handler.get()))
+    handler->Dump(output_stream);
+}
+
 const Flags Log::GetOptions() const {
   return m_options.load(std::memory_order_relaxed);
 }
@@ -222,6 +230,19 @@
   return true;
 }
 
+bool Log::DumpLogChannel(llvm::StringRef channel,
+                         llvm::ArrayRef<const char *> categories,
+                         llvm::raw_ostream &output_stream,
+                         llvm::raw_ostream &error_stream) {
+  auto iter = g_channel_map->find(channel);
+  if (iter == g_channel_map->end()) {
+    error_stream << llvm::formatv("Invalid log channel '{0}'.\n", channel);
+    return false;
+  }
+  iter->second.Dump(output_stream);
+  return true;
+}
+
 bool Log::ListChannelCategories(llvm::StringRef channel,
                                 llvm::raw_ostream &stream) {
   auto ch = g_channel_map->find(channel);
@@ -400,3 +421,9 @@
   }
   stream.flush();
 }
+
+char LogHandler::ID;
+char StreamLogHandler::ID;
+char CallbackLogHandler::ID;
+char BufferedLogHandler::ID;
+char RotatingLogHandler::ID;
Index: lldb/source/Core/Debugger.cpp
===================================================================
--- lldb/source/Core/Debugger.cpp
+++ lldb/source/Core/Debugger.cpp
@@ -1409,6 +1409,7 @@
 bool Debugger::EnableLog(llvm::StringRef channel,
                          llvm::ArrayRef<const char *> categories,
                          llvm::StringRef log_file, uint32_t log_options,
+                         size_t buffer_size, bool circular_buffer,
                          llvm::raw_ostream &error_stream) {
   const bool should_close = true;
 
@@ -1418,9 +1419,14 @@
     // For now when using the callback mode you always get thread & timestamp.
     log_options |=
         LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_THREAD_NAME;
+  } else if (buffer_size && circular_buffer) {
+    log_handler_sp = std::make_shared<RotatingLogHandler>(buffer_size);
   } else if (log_file.empty()) {
     log_handler_sp = std::make_shared<StreamLogHandler>(
         GetOutputFile().GetDescriptor(), !should_close);
+    if (buffer_size)
+      log_handler_sp =
+          std::make_shared<BufferedLogHandler>(buffer_size, log_handler_sp);
   } else {
     auto pos = m_stream_handlers.find(log_file);
     if (pos != m_stream_handlers.end())
@@ -1442,6 +1448,9 @@
 
       log_handler_sp = std::make_shared<StreamLogHandler>(
           (*file)->GetDescriptor(), should_close);
+      if (buffer_size)
+        log_handler_sp =
+            std::make_shared<BufferedLogHandler>(buffer_size, log_handler_sp);
       m_stream_handlers[log_file] = log_handler_sp;
     }
   }
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -428,9 +428,13 @@
     Desc<"Clears the current command history.">;
 }
 
-let Command = "log" in {
+let Command = "log enable" in {
   def log_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
-    Desc<"Set the destination file to log to.">;
+    Desc<"Set the destination file to log to. Cannot be combined with -c.">;
+  def log_buffer_size : Option<"buffer", "b">, Group<1>, Arg<"UnsignedInteger">,
+    Desc<"Set the size of the internal buffer to log to.">;
+  def log_circular : Option<"circular", "c">, Group<1>,
+    Desc<"Log to a circular in-memory buffer. Must be used in combination with -b. Cannot be combined with -f.">;
   def log_threadsafe : Option<"threadsafe", "t">, Group<1>,
     Desc<"Enable thread safe logging to avoid interweaved log lines.">;
   def log_verbose : Option<"verbose", "v">, Group<1>,
@@ -454,6 +458,11 @@
     Desc<"Prepend the names of files and function that generate the logs.">;
 }
 
+let Command = "log dump" in {
+  def log_dump_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
+    Desc<"Set the destination file to dump to.">;
+}
+
 let Command = "reproducer dump" in {
   def reproducer_provider : Option<"provider", "p">, Group<1>,
     EnumArg<"None", "ReproducerProviderType()">,
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/OptionValueUInt64.h"
 #include "lldb/Interpreter/Options.h"
 #include "lldb/Utility/Args.h"
 #include "lldb/Utility/FileSpec.h"
@@ -21,7 +22,10 @@
 using namespace lldb;
 using namespace lldb_private;
 
-#define LLDB_OPTIONS_log
+#define LLDB_OPTIONS_log_enable
+#include "CommandOptions.inc"
+
+#define LLDB_OPTIONS_log_dump
 #include "CommandOptions.inc"
 
 /// Common completion logic for log enable/disable.
@@ -89,6 +93,13 @@
         log_file.SetFile(option_arg, FileSpec::Style::native);
         FileSystem::Instance().Resolve(log_file);
         break;
+      case 'b':
+        error =
+            buffer_size.SetValueFromString(option_arg, eVarSetOperationAssign);
+        break;
+      case 'c':
+        circular_buffer = true;
+        break;
       case 't':
         log_options |= LLDB_LOG_OPTION_THREADSAFE;
         break;
@@ -126,16 +137,20 @@
     void OptionParsingStarting(ExecutionContext *execution_context) override {
       log_file.Clear();
       log_options = 0;
+      buffer_size.Clear();
+      circular_buffer = false;
     }
 
     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
-      return llvm::makeArrayRef(g_log_options);
+      return llvm::makeArrayRef(g_log_enable_options);
     }
 
     // Instance variables to hold the values for command options.
 
     FileSpec log_file;
     uint32_t log_options = 0;
+    OptionValueUInt64 buffer_size;
+    bool circular_buffer = false;
   };
 
   void
@@ -153,6 +168,18 @@
       return false;
     }
 
+    if (m_options.log_file && m_options.circular_buffer) {
+      result.AppendError("cannot combine logging to a file (-f) and to a "
+                         "circular in-memory buffer (-c).");
+      return false;
+    }
+
+    if (m_options.circular_buffer && !m_options.buffer_size.OptionWasSet()) {
+      result.AppendError("must specify a buffer size (-b) to log to a circular "
+                         "in-memory buffer (-c).");
+      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
@@ -164,9 +191,10 @@
 
     std::string error;
     llvm::raw_string_ostream error_stream(error);
-    bool success =
-        GetDebugger().EnableLog(channel, args.GetArgumentArrayRef(), log_file,
-                                m_options.log_options, error_stream);
+    bool success = GetDebugger().EnableLog(
+        channel, args.GetArgumentArrayRef(), log_file, m_options.log_options,
+        m_options.buffer_size.GetCurrentValue(), m_options.circular_buffer,
+        error_stream);
     result.GetErrorStream() << error_stream.str();
 
     if (success)
@@ -295,6 +323,122 @@
   }
 };
 
+class CommandObjectLogDump : public CommandObjectParsed {
+public:
+  CommandObjectLogDump(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "log dump",
+                            "dump circular in-memory logs", nullptr) {
+    CommandArgumentEntry arg1;
+    CommandArgumentEntry arg2;
+    CommandArgumentData channel_arg;
+    CommandArgumentData category_arg;
+
+    // Define the first (and only) variant of this arg.
+    channel_arg.arg_type = eArgTypeLogChannel;
+    channel_arg.arg_repetition = eArgRepeatPlain;
+
+    // There is only one variant this argument could be; put it into the
+    // argument entry.
+    arg1.push_back(channel_arg);
+
+    category_arg.arg_type = eArgTypeLogCategory;
+    category_arg.arg_repetition = eArgRepeatPlus;
+
+    arg2.push_back(category_arg);
+
+    // Push the data for the first argument into the m_arguments vector.
+    m_arguments.push_back(arg1);
+    m_arguments.push_back(arg2);
+  }
+
+  ~CommandObjectLogDump() override = default;
+
+  Options *GetOptions() override { return &m_options; }
+
+  class CommandOptions : public Options {
+  public:
+    CommandOptions() = default;
+
+    ~CommandOptions() override = default;
+
+    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+                          ExecutionContext *execution_context) override {
+      Status error;
+      const int short_option = m_getopt_table[option_idx].val;
+
+      switch (short_option) {
+      case 'f':
+        log_file.SetFile(option_arg, FileSpec::Style::native);
+        FileSystem::Instance().Resolve(log_file);
+        break;
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+
+      return error;
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      log_file.Clear();
+    }
+
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::makeArrayRef(g_log_dump_options);
+    }
+
+    FileSpec log_file;
+  };
+
+  void
+  HandleArgumentCompletion(CompletionRequest &request,
+                           OptionElementVector &opt_element_vector) override {
+    CompleteEnableDisable(request);
+  }
+
+protected:
+  bool DoExecute(Args &args, CommandReturnObject &result) override {
+    if (args.empty()) {
+      result.AppendErrorWithFormat(
+          "%s takes a log channel and one or more log types.\n",
+          m_cmd_name.c_str());
+      return false;
+    }
+
+    std::unique_ptr<llvm::raw_ostream> stream_up;
+    if (m_options.log_file) {
+      const File::OpenOptions flags = File::eOpenOptionWriteOnly |
+                                      File::eOpenOptionCanCreate |
+                                      File::eOpenOptionTruncate;
+      llvm::Expected<FileUP> file = FileSystem::Instance().Open(
+          m_options.log_file, flags, lldb::eFilePermissionsFileDefault, false);
+      if (!file) {
+        result.AppendErrorWithFormat("Unable to open log file '%s': %s",
+                                     m_options.log_file.GetCString(),
+                                     llvm::toString(file.takeError()).c_str());
+        return false;
+      }
+      stream_up = std::make_unique<llvm::raw_fd_ostream>(
+          (*file)->GetDescriptor(), /*shouldClose=*/true);
+    } else {
+      stream_up = std::make_unique<llvm::raw_fd_ostream>(
+          GetDebugger().GetOutputFile().GetDescriptor(), /*shouldClose=*/false);
+    }
+
+    const std::string channel = std::string(args[0].ref());
+    args.Shift(); // Shift off the channel
+    std::string error;
+    llvm::raw_string_ostream error_stream(error);
+    if (Log::DumpLogChannel(channel, args.GetArgumentArrayRef(), *stream_up,
+                            error_stream))
+      result.SetStatus(eReturnStatusSuccessFinishNoResult);
+    result.GetErrorStream() << error_stream.str();
+
+    return result.Succeeded();
+  }
+
+  CommandOptions m_options;
+};
+
 class CommandObjectLogTimerEnable : public CommandObjectParsed {
 public:
   // Constructors and Destructors
@@ -503,6 +647,8 @@
                  CommandObjectSP(new CommandObjectLogDisable(interpreter)));
   LoadSubCommand("list",
                  CommandObjectSP(new CommandObjectLogList(interpreter)));
+  LoadSubCommand("dump",
+                 CommandObjectSP(new CommandObjectLogDump(interpreter)));
   LoadSubCommand("timers",
                  CommandObjectSP(new CommandObjectLogTimer(interpreter)));
 }
Index: lldb/source/API/SBDebugger.cpp
===================================================================
--- lldb/source/API/SBDebugger.cpp
+++ lldb/source/API/SBDebugger.cpp
@@ -1620,7 +1620,8 @@
     std::string error;
     llvm::raw_string_ostream error_stream(error);
     return m_opaque_sp->EnableLog(channel, GetCategoryArray(categories), "",
-                                  log_options, error_stream);
+                                  log_options, /*buffer_size=*/0,
+                                  /*circular_buffer=*/false, error_stream);
   } else
     return false;
 }
Index: lldb/include/lldb/Utility/Log.h
===================================================================
--- lldb/include/lldb/Utility/Log.h
+++ lldb/include/lldb/Utility/Log.h
@@ -52,8 +52,15 @@
   virtual void Emit(llvm::StringRef message) = 0;
   void EmitThreadSafe(llvm::StringRef message);
 
+  /// LLVM RTTI support.
+  /// \{
+  virtual bool isA(const void *ClassID) const { return ClassID == &ID; }
+  static bool classof(const LogHandler *obj) { return obj->isA(&ID); }
+  /// \}
+
 private:
   std::mutex m_mutex;
+  static char ID;
 };
 
 class StreamLogHandler : public LogHandler {
@@ -62,8 +69,15 @@
 
   void Emit(llvm::StringRef message) override;
 
+  /// LLVM RTTI support.
+  /// \{
+  bool isA(const void *ClassID) const override { return ClassID == &ID; }
+  static bool classof(const LogHandler *obj) { return obj->isA(&ID); }
+  /// \}
+
 private:
   llvm::raw_fd_ostream m_stream;
+  static char ID;
 };
 
 class CallbackLogHandler : public LogHandler {
@@ -72,9 +86,16 @@
 
   void Emit(llvm::StringRef message) override;
 
+  /// LLVM RTTI support.
+  /// \{
+  bool isA(const void *ClassID) const override { return ClassID == &ID; }
+  static bool classof(const LogHandler *obj) { return obj->isA(&ID); }
+  /// \}
+
 private:
   lldb::LogOutputCallback m_callback;
   void *m_baton;
+  static char ID;
 };
 
 class BufferedLogHandler : public LogHandler {
@@ -83,11 +104,18 @@
 
   void Emit(llvm::StringRef message) override;
 
+  /// LLVM RTTI support.
+  /// \{
+  bool isA(const void *ClassID) const override { return ClassID == &ID; }
+  static bool classof(const LogHandler *obj) { return obj->isA(&ID); }
+  /// \}
+
 private:
   std::shared_ptr<LogHandler> m_delegate;
   std::unique_ptr<std::string[]> m_messages;
   const size_t m_size = 0;
   size_t m_next_index = 0;
+  static char ID;
 };
 
 class RotatingLogHandler : public LogHandler {
@@ -97,6 +125,12 @@
   void Emit(llvm::StringRef message) override;
   void Dump(llvm::raw_ostream &stream) const;
 
+  /// LLVM RTTI support.
+  /// \{
+  bool isA(const void *ClassID) const override { return ClassID == &ID; }
+  static bool classof(const LogHandler *obj) { return obj->isA(&ID); }
+  /// \}
+
 private:
   size_t NormalizeIndex(size_t i) const;
   size_t GetNumMessages() const;
@@ -106,6 +140,8 @@
   const size_t m_size = 0;
   size_t m_next_index = 0;
   size_t m_total_count = 0;
+
+  static char ID;
 };
 
 class Log final {
@@ -183,6 +219,11 @@
                                 llvm::ArrayRef<const char *> categories,
                                 llvm::raw_ostream &error_stream);
 
+  static bool DumpLogChannel(llvm::StringRef channel,
+                             llvm::ArrayRef<const char *> categories,
+                             llvm::raw_ostream &output_stream,
+                             llvm::raw_ostream &error_stream);
+
   static bool ListChannelCategories(llvm::StringRef channel,
                                     llvm::raw_ostream &stream);
 
@@ -272,6 +313,8 @@
 
   void Disable(uint32_t flags);
 
+  void Dump(llvm::raw_ostream &stream);
+
   typedef llvm::StringMap<Log> ChannelMap;
   static llvm::ManagedStatic<ChannelMap> g_channel_map;
 
Index: lldb/include/lldb/Core/Debugger.h
===================================================================
--- lldb/include/lldb/Core/Debugger.h
+++ lldb/include/lldb/Core/Debugger.h
@@ -245,6 +245,7 @@
   bool EnableLog(llvm::StringRef channel,
                  llvm::ArrayRef<const char *> categories,
                  llvm::StringRef log_file, uint32_t log_options,
+                 size_t buffer_size, bool circular_buffer,
                  llvm::raw_ostream &error_stream);
 
   void SetLoggingCallback(lldb::LogOutputCallback log_callback, void *baton);
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to