[Lldb-commits] [lldb] 6ff49af - [lldb] Introduce the concept of a log handler (NFC)

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Thu Jun 16 13:34:34 PDT 2022


Author: Jonas Devlieghere
Date: 2022-06-16T13:34:28-07:00
New Revision: 6ff49af33d093dd7e0659958185942d0d26a8b90

URL: https://github.com/llvm/llvm-project/commit/6ff49af33d093dd7e0659958185942d0d26a8b90
DIFF: https://github.com/llvm/llvm-project/commit/6ff49af33d093dd7e0659958185942d0d26a8b90.diff

LOG: [lldb] Introduce the concept of a log handler (NFC)

This patch introduces the concept of a log handlers. Log handlers allow
customizing the way log output is emitted. The StreamCallback class
tried to do something conceptually similar. The benefit of the log
handler interface is that you don't need to conform to llvm's
raw_ostream interface.

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

Added: 
    

Modified: 
    lldb/include/lldb/Core/Debugger.h
    lldb/include/lldb/Utility/Log.h
    lldb/source/Core/Debugger.cpp
    lldb/source/Utility/CMakeLists.txt
    lldb/source/Utility/Log.cpp
    lldb/tools/lldb-server/LLDBServerUtilities.cpp
    lldb/unittests/Core/CMakeLists.txt
    lldb/unittests/Utility/LogTest.cpp

Removed: 
    lldb/include/lldb/Utility/StreamCallback.h
    lldb/source/Utility/StreamCallback.cpp
    lldb/unittests/Core/StreamCallbackTest.cpp


################################################################################
diff  --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index 8005e03071b24..4cdc6b6237c7e 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -54,7 +54,9 @@ class ThreadPool;
 
 namespace lldb_private {
 class Address;
+class CallbackLogHandler;
 class CommandInterpreter;
+class LogHandler;
 class Process;
 class Stream;
 class SymbolContext;
@@ -553,8 +555,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
 
   llvm::Optional<uint64_t> m_current_event_id;
 
-  llvm::StringMap<std::weak_ptr<llvm::raw_ostream>> m_log_streams;
-  std::shared_ptr<llvm::raw_ostream> m_log_callback_stream_sp;
+  llvm::StringMap<std::weak_ptr<LogHandler>> m_stream_handlers;
+  std::shared_ptr<CallbackLogHandler> m_callback_handler_sp;
   ConstString m_instance_name;
   static LoadPluginCallbackType g_load_plugin_callback;
   typedef std::vector<llvm::sys::DynamicLibrary> LoadedPluginsList;

diff  --git a/lldb/include/lldb/Utility/Log.h b/lldb/include/lldb/Utility/Log.h
index 1ab12e0cfc1a1..d4ce30dde6c68 100644
--- a/lldb/include/lldb/Utility/Log.h
+++ b/lldb/include/lldb/Utility/Log.h
@@ -25,6 +25,7 @@
 #include <cstdarg>
 #include <cstdint>
 #include <memory>
+#include <mutex>
 #include <string>
 #include <type_traits>
 
@@ -45,6 +46,42 @@ class raw_ostream;
 // Logging Functions
 namespace lldb_private {
 
+class LogHandler {
+public:
+  virtual ~LogHandler() = default;
+  virtual void Emit(llvm::StringRef message) = 0;
+  void EmitThreadSafe(llvm::StringRef message);
+
+private:
+  std::mutex m_mutex;
+};
+
+class StreamLogHandler : public LogHandler {
+public:
+  StreamLogHandler(int fd, bool should_close, bool unbuffered);
+
+  void Emit(llvm::StringRef message) override;
+
+  static std::shared_ptr<StreamLogHandler> Create(int fd, bool unbuffered);
+
+private:
+  llvm::raw_fd_ostream m_stream;
+};
+
+class CallbackLogHandler : public LogHandler {
+public:
+  CallbackLogHandler(lldb::LogOutputCallback callback, void *baton);
+
+  void Emit(llvm::StringRef message) override;
+
+  static std::shared_ptr<CallbackLogHandler>
+  Create(lldb::LogOutputCallback callback, void *baton);
+
+private:
+  lldb::LogOutputCallback m_callback;
+  void *m_baton;
+};
+
 class Log final {
 public:
   /// The underlying type of all log channel enums. Declare them as:
@@ -111,7 +148,7 @@ class Log final {
   static void Unregister(llvm::StringRef name);
 
   static bool
-  EnableLogChannel(const std::shared_ptr<llvm::raw_ostream> &log_stream_sp,
+  EnableLogChannel(const std::shared_ptr<LogHandler> &log_handler_sp,
                    uint32_t log_options, llvm::StringRef channel,
                    llvm::ArrayRef<const char *> categories,
                    llvm::raw_ostream &error_stream);
@@ -188,7 +225,7 @@ class Log final {
   // Their modification however, is still protected by this mutex.
   llvm::sys::RWMutex m_mutex;
 
-  std::shared_ptr<llvm::raw_ostream> m_stream_sp;
+  std::shared_ptr<LogHandler> m_handler;
   std::atomic<uint32_t> m_options{0};
   std::atomic<MaskType> m_mask{0};
 
@@ -199,13 +236,13 @@ class Log final {
   void Format(llvm::StringRef file, llvm::StringRef function,
               const llvm::formatv_object_base &payload);
 
-  std::shared_ptr<llvm::raw_ostream> GetStream() {
+  std::shared_ptr<LogHandler> GetHandler() {
     llvm::sys::ScopedReader lock(m_mutex);
-    return m_stream_sp;
+    return m_handler;
   }
 
-  void Enable(const std::shared_ptr<llvm::raw_ostream> &stream_sp,
-              uint32_t options, uint32_t flags);
+  void Enable(const std::shared_ptr<LogHandler> &handler_sp, uint32_t options,
+              uint32_t flags);
 
   void Disable(uint32_t flags);
 

diff  --git a/lldb/include/lldb/Utility/StreamCallback.h b/lldb/include/lldb/Utility/StreamCallback.h
deleted file mode 100644
index d234cbea85c61..0000000000000
--- a/lldb/include/lldb/Utility/StreamCallback.h
+++ /dev/null
@@ -1,35 +0,0 @@
-//===-- StreamCallback.h -----------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLDB_UTILITY_STREAMCALLBACK_H
-#define LLDB_UTILITY_STREAMCALLBACK_H
-
-#include "lldb/lldb-types.h"
-#include "llvm/Support/raw_ostream.h"
-
-#include <cstddef>
-#include <cstdint>
-
-namespace lldb_private {
-
-class StreamCallback : public llvm::raw_ostream {
-public:
-  StreamCallback(lldb::LogOutputCallback callback, void *baton);
-  ~StreamCallback() override = default;
-
-private:
-  lldb::LogOutputCallback m_callback;
-  void *m_baton;
-
-  void write_impl(const char *Ptr, size_t Size) override;
-  uint64_t current_pos() const override;
-};
-
-} // namespace lldb_private
-
-#endif // LLDB_UTILITY_STREAMCALLBACK_H

diff  --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 5f0b1cc66c0d3..08d95c0ea6545 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -51,7 +51,6 @@
 #include "lldb/Utility/ReproducerProvider.h"
 #include "lldb/Utility/State.h"
 #include "lldb/Utility/Stream.h"
-#include "lldb/Utility/StreamCallback.h"
 #include "lldb/Utility/StreamString.h"
 
 #if defined(_WIN32)
@@ -758,8 +757,7 @@ Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton)
       m_forward_listener_sp(), m_clear_once() {
   m_instance_name.SetString(llvm::formatv("debugger_{0}", GetID()).str());
   if (log_callback)
-    m_log_callback_stream_sp =
-        std::make_shared<StreamCallback>(log_callback, baton);
+    m_callback_handler_sp = CallbackLogHandler::Create(log_callback, baton);
   m_command_interpreter_up->Initialize();
   // Always add our default platform to the platform list
   PlatformSP default_platform_sp(Platform::GetHostPlatform());
@@ -1292,8 +1290,7 @@ void Debugger::SetLoggingCallback(lldb::LogOutputCallback log_callback,
   // For simplicity's sake, I am not going to deal with how to close down any
   // open logging streams, I just redirect everything from here on out to the
   // callback.
-  m_log_callback_stream_sp =
-      std::make_shared<StreamCallback>(log_callback, baton);
+  m_callback_handler_sp = CallbackLogHandler::Create(log_callback, baton);
 }
 
 static void PrivateReportProgress(Debugger &debugger, uint64_t progress_id,
@@ -1412,22 +1409,21 @@ bool Debugger::EnableLog(llvm::StringRef channel,
                          llvm::StringRef log_file, uint32_t log_options,
                          llvm::raw_ostream &error_stream) {
   const bool should_close = true;
-  const bool unbuffered = true;
 
-  std::shared_ptr<llvm::raw_ostream> log_stream_sp;
-  if (m_log_callback_stream_sp) {
-    log_stream_sp = m_log_callback_stream_sp;
+  std::shared_ptr<LogHandler> log_handler_sp;
+  if (m_callback_handler_sp) {
+    log_handler_sp = m_callback_handler_sp;
     // 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 (log_file.empty()) {
-    log_stream_sp = std::make_shared<llvm::raw_fd_ostream>(
-        GetOutputFile().GetDescriptor(), !should_close, unbuffered);
+    log_handler_sp = StreamLogHandler::Create(GetOutputFile().GetDescriptor(),
+                                              !should_close);
   } else {
-    auto pos = m_log_streams.find(log_file);
-    if (pos != m_log_streams.end())
-      log_stream_sp = pos->second.lock();
-    if (!log_stream_sp) {
+    auto pos = m_stream_handlers.find(log_file);
+    if (pos != m_stream_handlers.end())
+      log_handler_sp = pos->second.lock();
+    if (!log_handler_sp) {
       File::OpenOptions flags =
           File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate;
       if (log_options & LLDB_LOG_OPTION_APPEND)
@@ -1442,18 +1438,18 @@ bool Debugger::EnableLog(llvm::StringRef channel,
         return false;
       }
 
-      log_stream_sp = std::make_shared<llvm::raw_fd_ostream>(
-          (*file)->GetDescriptor(), should_close, unbuffered);
-      m_log_streams[log_file] = log_stream_sp;
+      log_handler_sp =
+          StreamLogHandler::Create((*file)->GetDescriptor(), should_close);
+      m_stream_handlers[log_file] = log_handler_sp;
     }
   }
-  assert(log_stream_sp);
+  assert(log_handler_sp);
 
   if (log_options == 0)
     log_options =
         LLDB_LOG_OPTION_PREPEND_THREAD_NAME | LLDB_LOG_OPTION_THREADSAFE;
 
-  return Log::EnableLogChannel(log_stream_sp, log_options, channel, categories,
+  return Log::EnableLogChannel(log_handler_sp, log_options, channel, categories,
                                error_stream);
 }
 

diff  --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt
index f76ba27757085..1a92c033fac41 100644
--- a/lldb/source/Utility/CMakeLists.txt
+++ b/lldb/source/Utility/CMakeLists.txt
@@ -56,7 +56,6 @@ add_lldb_library(lldbUtility
   State.cpp
   Status.cpp
   Stream.cpp
-  StreamCallback.cpp
   StreamString.cpp
   StringExtractor.cpp
   StringExtractorGDBRemote.cpp

diff  --git a/lldb/source/Utility/Log.cpp b/lldb/source/Utility/Log.cpp
index 344a7b102e99f..b9b79636d0dcf 100644
--- a/lldb/source/Utility/Log.cpp
+++ b/lldb/source/Utility/Log.cpp
@@ -84,14 +84,14 @@ uint32_t Log::GetFlags(llvm::raw_ostream &stream, const ChannelMap::value_type &
   return flags;
 }
 
-void Log::Enable(const std::shared_ptr<llvm::raw_ostream> &stream_sp,
+void Log::Enable(const std::shared_ptr<LogHandler> &handler_sp,
                  uint32_t options, uint32_t flags) {
   llvm::sys::ScopedWriter lock(m_mutex);
 
   MaskType mask = m_mask.fetch_or(flags, std::memory_order_relaxed);
   if (mask | flags) {
     m_options.store(options, std::memory_order_relaxed);
-    m_stream_sp = stream_sp;
+    m_handler = handler_sp;
     m_channel.log_ptr.store(this, std::memory_order_relaxed);
   }
 }
@@ -101,7 +101,7 @@ void Log::Disable(uint32_t flags) {
 
   MaskType mask = m_mask.fetch_and(~flags, std::memory_order_relaxed);
   if (!(mask & ~flags)) {
-    m_stream_sp.reset();
+    m_handler.reset();
     m_channel.log_ptr.store(nullptr, std::memory_order_relaxed);
   }
 }
@@ -191,10 +191,10 @@ void Log::Unregister(llvm::StringRef name) {
   g_channel_map->erase(iter);
 }
 
-bool Log::EnableLogChannel(
-    const std::shared_ptr<llvm::raw_ostream> &log_stream_sp,
-    uint32_t log_options, llvm::StringRef channel,
-    llvm::ArrayRef<const char *> categories, llvm::raw_ostream &error_stream) {
+bool Log::EnableLogChannel(const std::shared_ptr<LogHandler> &log_handler_sp,
+                           uint32_t log_options, llvm::StringRef channel,
+                           llvm::ArrayRef<const char *> categories,
+                           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);
@@ -203,7 +203,7 @@ bool Log::EnableLogChannel(
   uint32_t flags = categories.empty()
                        ? iter->second.m_channel.default_flags
                        : GetFlags(error_stream, *iter, categories);
-  iter->second.Enable(log_stream_sp, log_options, flags);
+  iter->second.Enable(log_handler_sp, log_options, flags);
   return true;
 }
 
@@ -314,20 +314,15 @@ void Log::WriteHeader(llvm::raw_ostream &OS, llvm::StringRef file,
 void Log::WriteMessage(const std::string &message) {
   // Make a copy of our stream shared pointer in case someone disables our log
   // while we are logging and releases the stream
-  auto stream_sp = GetStream();
-  if (!stream_sp)
+  auto handler_sp = GetHandler();
+  if (!handler_sp)
     return;
 
   Flags options = GetOptions();
-  if (options.Test(LLDB_LOG_OPTION_THREADSAFE)) {
-    static std::recursive_mutex g_LogThreadedMutex;
-    std::lock_guard<std::recursive_mutex> guard(g_LogThreadedMutex);
-    *stream_sp << message;
-    stream_sp->flush();
-  } else {
-    *stream_sp << message;
-    stream_sp->flush();
-  }
+  if (options.Test(LLDB_LOG_OPTION_THREADSAFE))
+    handler_sp->EmitThreadSafe(message);
+  else
+    handler_sp->Emit(message);
 }
 
 void Log::Format(llvm::StringRef file, llvm::StringRef function,
@@ -338,3 +333,35 @@ void Log::Format(llvm::StringRef file, llvm::StringRef function,
   message << payload << "\n";
   WriteMessage(message.str());
 }
+
+void LogHandler::EmitThreadSafe(llvm::StringRef message) {
+  std::lock_guard<std::mutex> guard(m_mutex);
+  Emit(message);
+}
+
+StreamLogHandler::StreamLogHandler(int fd, bool should_close, bool unbuffered)
+    : m_stream(fd, should_close, unbuffered) {}
+
+void StreamLogHandler::Emit(llvm::StringRef message) {
+  m_stream << message;
+  m_stream.flush();
+}
+
+std::shared_ptr<StreamLogHandler> StreamLogHandler::Create(int fd,
+                                                           bool should_close) {
+  constexpr const bool unbuffered = true;
+  return std::make_shared<StreamLogHandler>(fd, should_close, unbuffered);
+}
+
+CallbackLogHandler::CallbackLogHandler(lldb::LogOutputCallback callback,
+                                       void *baton)
+    : m_callback(callback), m_baton(baton) {}
+
+void CallbackLogHandler::Emit(llvm::StringRef message) {
+  m_callback(message.data(), m_baton);
+}
+
+std::shared_ptr<CallbackLogHandler>
+CallbackLogHandler::Create(lldb::LogOutputCallback callback, void *baton) {
+  return std::make_shared<CallbackLogHandler>(callback, baton);
+}

diff  --git a/lldb/source/Utility/StreamCallback.cpp b/lldb/source/Utility/StreamCallback.cpp
deleted file mode 100644
index c10f678d7a284..0000000000000
--- a/lldb/source/Utility/StreamCallback.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-//===-- StreamCallback.cpp ------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "lldb/Utility/StreamCallback.h"
-
-#include <string>
-
-using namespace lldb_private;
-
-StreamCallback::StreamCallback(lldb::LogOutputCallback callback, void *baton)
-    : llvm::raw_ostream(true), m_callback(callback), m_baton(baton) {}
-
-void StreamCallback::write_impl(const char *Ptr, size_t Size) {
-  m_callback(std::string(Ptr, Size).c_str(), m_baton);
-}
-
-uint64_t StreamCallback::current_pos() const { return 0; }

diff  --git a/lldb/tools/lldb-server/LLDBServerUtilities.cpp b/lldb/tools/lldb-server/LLDBServerUtilities.cpp
index a427af4800f78..c3a8df19e969e 100644
--- a/lldb/tools/lldb-server/LLDBServerUtilities.cpp
+++ b/lldb/tools/lldb-server/LLDBServerUtilities.cpp
@@ -18,21 +18,37 @@
 
 using namespace lldb;
 using namespace lldb_private::lldb_server;
+using namespace lldb_private;
 using namespace llvm;
 
-static std::shared_ptr<raw_ostream> GetLogStream(StringRef log_file) {
+class TestLogHandler : public LogHandler {
+public:
+  TestLogHandler(std::shared_ptr<llvm::raw_ostream> stream_sp)
+      : m_stream_sp(stream_sp) {}
+
+  void Emit(llvm::StringRef message) override {
+    (*m_stream_sp) << message;
+    m_stream_sp->flush();
+  }
+
+private:
+  std::shared_ptr<raw_ostream> m_stream_sp;
+};
+
+static std::shared_ptr<TestLogHandler> GetLogStream(StringRef log_file) {
   if (!log_file.empty()) {
     std::error_code EC;
-    std::shared_ptr<raw_ostream> stream_sp = std::make_shared<raw_fd_ostream>(
+    auto stream_sp = std::make_shared<raw_fd_ostream>(
         log_file, EC, sys::fs::OF_TextWithCRLF | sys::fs::OF_Append);
     if (!EC)
-      return stream_sp;
+      return std::make_shared<TestLogHandler>(stream_sp);
     errs() << llvm::formatv(
         "Failed to open log file `{0}`: {1}\nWill log to stderr instead.\n",
         log_file, EC.message());
   }
   // No need to delete the stderr stream.
-  return std::shared_ptr<raw_ostream>(&errs(), [](raw_ostream *) {});
+  return std::make_shared<TestLogHandler>(
+      std::shared_ptr<raw_ostream>(&errs(), [](raw_ostream *) {}));
 }
 
 bool LLDBServerUtilities::SetupLogging(const std::string &log_file,

diff  --git a/lldb/unittests/Core/CMakeLists.txt b/lldb/unittests/Core/CMakeLists.txt
index bd4acef40f77a..6e2dd03536d7a 100644
--- a/lldb/unittests/Core/CMakeLists.txt
+++ b/lldb/unittests/Core/CMakeLists.txt
@@ -8,7 +8,6 @@ add_lldb_unittest(LLDBCoreTests
   RichManglingContextTest.cpp
   SourceLocationSpecTest.cpp
   SourceManagerTest.cpp
-  StreamCallbackTest.cpp
   UniqueCStringMapTest.cpp
 
   LINK_LIBS

diff  --git a/lldb/unittests/Core/StreamCallbackTest.cpp b/lldb/unittests/Core/StreamCallbackTest.cpp
deleted file mode 100644
index d0e50b6864dd6..0000000000000
--- a/lldb/unittests/Core/StreamCallbackTest.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-//===-- StreamCallbackTest.cpp --------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "lldb/Utility/StreamCallback.h"
-#include "gtest/gtest.h"
-
-using namespace lldb;
-using namespace lldb_private;
-
-static char test_baton;
-static size_t callback_count = 0;
-static void TestCallback(const char *data, void *baton) {
-  EXPECT_STREQ("Foobar", data);
-  EXPECT_EQ(&test_baton, baton);
-  ++callback_count;
-}
-
-TEST(StreamCallbackTest, Callback) {
-  StreamCallback stream(TestCallback, &test_baton);
-  stream << "Foobar";
-  EXPECT_EQ(1u, callback_count);
-}

diff  --git a/lldb/unittests/Utility/LogTest.cpp b/lldb/unittests/Utility/LogTest.cpp
index 533f8620c46f5..2b61c99630ea6 100644
--- a/lldb/unittests/Utility/LogTest.cpp
+++ b/lldb/unittests/Utility/LogTest.cpp
@@ -39,13 +39,13 @@ template <> Log::Channel &LogChannelFor<TestChannel>() { return test_channel; }
 } // namespace lldb_private
 
 // Wrap enable, disable and list functions to make them easier to test.
-static bool EnableChannel(std::shared_ptr<llvm::raw_ostream> stream_sp,
+static bool EnableChannel(std::shared_ptr<LogHandler> log_handler_sp,
                           uint32_t log_options, llvm::StringRef channel,
                           llvm::ArrayRef<const char *> categories,
                           std::string &error) {
   error.clear();
   llvm::raw_string_ostream error_stream(error);
-  return Log::EnableLogChannel(stream_sp, log_options, channel, categories,
+  return Log::EnableLogChannel(log_handler_sp, log_options, channel, categories,
                                error_stream);
 }
 
@@ -68,9 +68,7 @@ namespace {
 struct LogChannelTest : public ::testing::Test {
   void TearDown() override { Log::DisableAllLogChannels(); }
 
-  static void SetUpTestCase() {
-    Log::Register("chan", test_channel);
-  }
+  static void SetUpTestCase() { Log::Register("chan", test_channel); }
 
   static void TearDownTestCase() {
     Log::Unregister("chan");
@@ -78,18 +76,27 @@ struct LogChannelTest : public ::testing::Test {
   }
 };
 
+class TestLogHandler : public LogHandler {
+public:
+  TestLogHandler() : m_messages(), m_stream(m_messages) {}
+
+  void Emit(llvm::StringRef message) override { m_stream << message; }
+
+  llvm::SmallString<0> m_messages;
+  llvm::raw_svector_ostream m_stream;
+};
+
 // A test fixture which provides tests with a pre-registered and pre-enabled
 // channel. Additionally, the messages written to that channel are captured and
 // made available via getMessage().
 class LogChannelEnabledTest : public LogChannelTest {
-  llvm::SmallString<0> m_messages;
-  std::shared_ptr<llvm::raw_svector_ostream> m_stream_sp =
-      std::make_shared<llvm::raw_svector_ostream>(m_messages);
+  std::shared_ptr<TestLogHandler> m_log_handler_sp =
+      std::make_shared<TestLogHandler>();
   Log *m_log;
   size_t m_consumed_bytes = 0;
 
 protected:
-  std::shared_ptr<llvm::raw_ostream> getStream() { return m_stream_sp; }
+  std::shared_ptr<LogHandler> getLogHandler() { return m_log_handler_sp; }
   Log *getLog() { return m_log; }
   llvm::StringRef takeOutput();
   llvm::StringRef logAndTakeOutput(llvm::StringRef Message);
@@ -103,19 +110,21 @@ void LogChannelEnabledTest::SetUp() {
   LogChannelTest::SetUp();
 
   std::string error;
-  ASSERT_TRUE(EnableChannel(m_stream_sp, 0, "chan", {}, error));
+  ASSERT_TRUE(EnableChannel(m_log_handler_sp, 0, "chan", {}, error));
 
   m_log = GetLog(TestChannel::FOO);
   ASSERT_NE(nullptr, m_log);
 }
 
 llvm::StringRef LogChannelEnabledTest::takeOutput() {
-  llvm::StringRef result = m_stream_sp->str().drop_front(m_consumed_bytes);
-  m_consumed_bytes+= result.size();
+  llvm::StringRef result =
+      m_log_handler_sp->m_stream.str().drop_front(m_consumed_bytes);
+  m_consumed_bytes += result.size();
   return result;
 }
 
-llvm::StringRef LogChannelEnabledTest::logAndTakeOutput(llvm::StringRef Message) {
+llvm::StringRef
+LogChannelEnabledTest::logAndTakeOutput(llvm::StringRef Message) {
   LLDB_LOG(m_log, "{0}", Message);
   return takeOutput();
 }
@@ -138,33 +147,48 @@ TEST(LogTest, Unregister) {
   Log::Register("chan", test_channel);
   EXPECT_EQ(nullptr, GetLog(TestChannel::FOO));
   std::string message;
-  std::shared_ptr<llvm::raw_string_ostream> stream_sp(
-      new llvm::raw_string_ostream(message));
-  EXPECT_TRUE(Log::EnableLogChannel(stream_sp, 0, "chan", {"foo"}, llvm::nulls()));
+  auto log_handler_sp = std::make_shared<TestLogHandler>();
+  EXPECT_TRUE(
+      Log::EnableLogChannel(log_handler_sp, 0, "chan", {"foo"}, llvm::nulls()));
   EXPECT_NE(nullptr, GetLog(TestChannel::FOO));
   Log::Unregister("chan");
   EXPECT_EQ(nullptr, GetLog(TestChannel::FOO));
 }
 
+namespace {
+static char test_baton;
+static size_t callback_count = 0;
+static void TestCallback(const char *data, void *baton) {
+  EXPECT_STREQ("Foobar", data);
+  EXPECT_EQ(&test_baton, baton);
+  ++callback_count;
+}
+} // namespace
+
+TEST(LogTest, CallbackLogHandler) {
+  CallbackLogHandler handler(TestCallback, &test_baton);
+  handler.Emit("Foobar");
+  EXPECT_EQ(1u, callback_count);
+}
+
 TEST_F(LogChannelTest, Enable) {
   EXPECT_EQ(nullptr, GetLog(TestChannel::FOO));
   std::string message;
-  std::shared_ptr<llvm::raw_string_ostream> stream_sp(
-      new llvm::raw_string_ostream(message));
+  auto log_handler_sp = std::make_shared<TestLogHandler>();
   std::string error;
-  ASSERT_FALSE(EnableChannel(stream_sp, 0, "chanchan", {}, error));
+  ASSERT_FALSE(EnableChannel(log_handler_sp, 0, "chanchan", {}, error));
   EXPECT_EQ("Invalid log channel 'chanchan'.\n", error);
 
-  EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {}, error));
+  EXPECT_TRUE(EnableChannel(log_handler_sp, 0, "chan", {}, error));
   EXPECT_NE(nullptr, GetLog(TestChannel::FOO));
   EXPECT_EQ(nullptr, GetLog(TestChannel::BAR));
   EXPECT_NE(nullptr, GetLog(TestChannel::FOO | TestChannel::BAR));
 
-  EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {"bar"}, error));
+  EXPECT_TRUE(EnableChannel(log_handler_sp, 0, "chan", {"bar"}, error));
   EXPECT_NE(nullptr, GetLog(TestChannel::FOO));
   EXPECT_NE(nullptr, GetLog(TestChannel::BAR));
 
-  EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {"baz"}, error));
+  EXPECT_TRUE(EnableChannel(log_handler_sp, 0, "chan", {"baz"}, error));
   EXPECT_NE(std::string::npos, error.find("unrecognized log category 'baz'"))
       << "error: " << error;
 }
@@ -172,11 +196,10 @@ TEST_F(LogChannelTest, Enable) {
 TEST_F(LogChannelTest, EnableOptions) {
   EXPECT_EQ(nullptr, GetLog(TestChannel::FOO));
   std::string message;
-  std::shared_ptr<llvm::raw_string_ostream> stream_sp(
-      new llvm::raw_string_ostream(message));
+  auto log_handler_sp = std::make_shared<TestLogHandler>();
   std::string error;
-  EXPECT_TRUE(
-      EnableChannel(stream_sp, LLDB_LOG_OPTION_VERBOSE, "chan", {}, error));
+  EXPECT_TRUE(EnableChannel(log_handler_sp, LLDB_LOG_OPTION_VERBOSE, "chan", {},
+                            error));
 
   Log *log = GetLog(TestChannel::FOO);
   ASSERT_NE(nullptr, log);
@@ -186,10 +209,9 @@ TEST_F(LogChannelTest, EnableOptions) {
 TEST_F(LogChannelTest, Disable) {
   EXPECT_EQ(nullptr, GetLog(TestChannel::FOO));
   std::string message;
-  std::shared_ptr<llvm::raw_string_ostream> stream_sp(
-      new llvm::raw_string_ostream(message));
+  auto log_handler_sp = std::make_shared<TestLogHandler>();
   std::string error;
-  EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {"foo", "bar"}, error));
+  EXPECT_TRUE(EnableChannel(log_handler_sp, 0, "chan", {"foo", "bar"}, error));
   EXPECT_NE(nullptr, GetLog(TestChannel::FOO));
   EXPECT_NE(nullptr, GetLog(TestChannel::BAR));
 
@@ -226,12 +248,12 @@ TEST_F(LogChannelTest, List) {
 TEST_F(LogChannelEnabledTest, log_options) {
   std::string Err;
   EXPECT_EQ("Hello World\n", logAndTakeOutput("Hello World"));
-  EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_THREADSAFE, "chan", {},
-                            Err));
+  EXPECT_TRUE(EnableChannel(getLogHandler(), LLDB_LOG_OPTION_THREADSAFE, "chan",
+                            {}, Err));
   EXPECT_EQ("Hello World\n", logAndTakeOutput("Hello World"));
 
   {
-    EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_PREPEND_SEQUENCE,
+    EXPECT_TRUE(EnableChannel(getLogHandler(), LLDB_LOG_OPTION_PREPEND_SEQUENCE,
                               "chan", {}, Err));
     llvm::StringRef Msg = logAndTakeOutput("Hello World");
     int seq_no;
@@ -239,19 +261,23 @@ TEST_F(LogChannelEnabledTest, log_options) {
   }
 
   {
-    EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION,
-                              "chan", {}, Err));
+    EXPECT_TRUE(EnableChannel(getLogHandler(),
+                              LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION, "chan", {},
+                              Err));
     llvm::StringRef Msg = logAndTakeOutput("Hello World");
     char File[12];
     char Function[17];
-      
-    sscanf(Msg.str().c_str(), "%[^:]:%s                                 Hello World", File, Function);
+
+    sscanf(Msg.str().c_str(),
+           "%[^:]:%s                                 Hello World", File,
+           Function);
     EXPECT_STRCASEEQ("LogTest.cpp", File);
     EXPECT_STREQ("logAndTakeOutput", Function);
   }
 
-  EXPECT_TRUE(EnableChannel(
-      getStream(), LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD, "chan", {}, Err));
+  EXPECT_TRUE(EnableChannel(getLogHandler(),
+                            LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD, "chan", {},
+                            Err));
   EXPECT_EQ(llvm::formatv("[{0,0+4}/{1,0+4}] Hello World\n", ::getpid(),
                           llvm::get_threadid())
                 .str(),
@@ -300,7 +326,7 @@ TEST_F(LogChannelEnabledTest, LogVerboseThread) {
   // (with 
diff erent log options).
   std::thread log_thread([this] { LLDB_LOGV(getLog(), "Hello World"); });
   EXPECT_TRUE(
-      EnableChannel(getStream(), LLDB_LOG_OPTION_VERBOSE, "chan", {}, err));
+      EnableChannel(getLogHandler(), LLDB_LOG_OPTION_VERBOSE, "chan", {}, err));
   log_thread.join();
 
   // The log thread either managed to write to the log, or it didn't. In either


        


More information about the lldb-commits mailing list