Index: lldb/include/lldb/Core/Debugger.h =================================================================== --- lldb/include/lldb/Core/Debugger.h +++ lldb/include/lldb/Core/Debugger.h @@ -53,6 +53,8 @@ } namespace lldb_private { +class LogHandler; +class CallbackLogHandler; class Address; class CommandInterpreter; class Process; @@ -553,8 +555,8 @@ llvm::Optional m_current_event_id; - llvm::StringMap> m_log_streams; - std::shared_ptr m_log_callback_stream_sp; + llvm::StringMap> m_stream_handlers; + std::shared_ptr m_callback_handler_sp; ConstString m_instance_name; static LoadPluginCallbackType g_load_plugin_callback; typedef std::vector LoadedPluginsList; Index: lldb/include/lldb/Utility/Log.h =================================================================== --- lldb/include/lldb/Utility/Log.h +++ lldb/include/lldb/Utility/Log.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,45 @@ // Logging Functions namespace lldb_private { +class LogHandler { +public: + virtual ~LogHandler() = default; + virtual void Emit(llvm::StringRef message) = 0; + virtual void Flush() = 0; + std::mutex &GetMutex() { return m_mutex; } + +private: + std::mutex m_mutex; +}; + +class StreamLogHandler : public LogHandler { +public: + StreamLogHandler(int fd, bool should_close, bool unbuffered); + + void Emit(llvm::StringRef message) override; + void Flush() override; + + static std::shared_ptr 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; + void Flush() override; + + static std::shared_ptr + 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 +151,7 @@ static void Unregister(llvm::StringRef name); static bool - EnableLogChannel(const std::shared_ptr &log_stream_sp, + EnableLogChannel(const std::shared_ptr &log_handler_sp, uint32_t log_options, llvm::StringRef channel, llvm::ArrayRef categories, llvm::raw_ostream &error_stream); @@ -188,7 +228,7 @@ // Their modification however, is still protected by this mutex. llvm::sys::RWMutex m_mutex; - std::shared_ptr m_stream_sp; + std::shared_ptr m_handler; std::atomic m_options{0}; std::atomic m_mask{0}; @@ -199,13 +239,13 @@ void Format(llvm::StringRef file, llvm::StringRef function, const llvm::formatv_object_base &payload); - std::shared_ptr GetStream() { + std::shared_ptr GetHandler() { llvm::sys::ScopedReader lock(m_mutex); - return m_stream_sp; + return m_handler; } - void Enable(const std::shared_ptr &stream_sp, - uint32_t options, uint32_t flags); + void Enable(const std::shared_ptr &handler_sp, uint32_t options, + uint32_t flags); void Disable(uint32_t flags); Index: lldb/include/lldb/Utility/StreamCallback.h =================================================================== --- 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 -#include - -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 Index: lldb/source/Core/Debugger.cpp =================================================================== --- lldb/source/Core/Debugger.cpp +++ 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 @@ 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(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 @@ // 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(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 @@ 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 log_stream_sp; - if (m_log_callback_stream_sp) { - log_stream_sp = m_log_callback_stream_sp; + std::shared_ptr 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( - 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 @@ return false; } - log_stream_sp = std::make_shared( - (*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); } Index: lldb/source/Utility/CMakeLists.txt =================================================================== --- lldb/source/Utility/CMakeLists.txt +++ lldb/source/Utility/CMakeLists.txt @@ -56,7 +56,6 @@ State.cpp Status.cpp Stream.cpp - StreamCallback.cpp StreamString.cpp StringExtractor.cpp StringExtractorGDBRemote.cpp Index: lldb/source/Utility/Log.cpp =================================================================== --- lldb/source/Utility/Log.cpp +++ lldb/source/Utility/Log.cpp @@ -84,14 +84,14 @@ return flags; } -void Log::Enable(const std::shared_ptr &stream_sp, +void Log::Enable(const std::shared_ptr &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 @@ 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 @@ g_channel_map->erase(iter); } -bool Log::EnableLogChannel( - const std::shared_ptr &log_stream_sp, - uint32_t log_options, llvm::StringRef channel, - llvm::ArrayRef categories, llvm::raw_ostream &error_stream) { +bool Log::EnableLogChannel(const std::shared_ptr &log_handler_sp, + uint32_t log_options, llvm::StringRef channel, + llvm::ArrayRef 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 @@ 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,19 +314,18 @@ 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 guard(g_LogThreadedMutex); - *stream_sp << message; - stream_sp->flush(); + std::lock_guard guard(handler_sp->GetMutex()); + handler_sp->Emit(message); + handler_sp->Flush(); } else { - *stream_sp << message; - stream_sp->flush(); + handler_sp->Emit(message); + handler_sp->Flush(); } } @@ -338,3 +337,31 @@ message << payload << "\n"; WriteMessage(message.str()); } + +StreamLogHandler::StreamLogHandler(int fd, bool should_close, bool unbuffered) + : m_stream(fd, should_close, unbuffered) {} + +void StreamLogHandler::Emit(llvm::StringRef message) { m_stream << message; } + +void StreamLogHandler::Flush() { m_stream.flush(); } + +std::shared_ptr StreamLogHandler::Create(int fd, + bool should_close) { + constexpr const bool unbuffered = true; + return std::make_shared(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); +} + +void CallbackLogHandler::Flush() {} + +std::shared_ptr +CallbackLogHandler::Create(lldb::LogOutputCallback callback, void *baton) { + return std::make_shared(callback, baton); +} Index: lldb/source/Utility/StreamCallback.cpp =================================================================== --- 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 - -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; } Index: lldb/tools/lldb-server/LLDBServerUtilities.cpp =================================================================== --- lldb/tools/lldb-server/LLDBServerUtilities.cpp +++ lldb/tools/lldb-server/LLDBServerUtilities.cpp @@ -18,21 +18,35 @@ using namespace lldb; using namespace lldb_private::lldb_server; +using namespace lldb_private; using namespace llvm; -static std::shared_ptr GetLogStream(StringRef log_file) { +class TestLogHandler : public LogHandler { +public: + TestLogHandler(std::shared_ptr stream_sp) + : m_stream_sp(stream_sp) {} + + void Emit(llvm::StringRef message) override { (*m_stream_sp) << message; } + void Flush() override { m_stream_sp->flush(); } + +private: + std::shared_ptr m_stream_sp; +}; + +static std::shared_ptr GetLogStream(StringRef log_file) { if (!log_file.empty()) { std::error_code EC; - std::shared_ptr stream_sp = std::make_shared( + auto stream_sp = std::make_shared( log_file, EC, sys::fs::OF_TextWithCRLF | sys::fs::OF_Append); if (!EC) - return stream_sp; + return std::make_shared(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(&errs(), [](raw_ostream *) {}); + return std::make_shared( + std::shared_ptr(&errs(), [](raw_ostream *) {})); } bool LLDBServerUtilities::SetupLogging(const std::string &log_file, Index: lldb/unittests/Core/CMakeLists.txt =================================================================== --- lldb/unittests/Core/CMakeLists.txt +++ lldb/unittests/Core/CMakeLists.txt @@ -8,7 +8,6 @@ RichManglingContextTest.cpp SourceLocationSpecTest.cpp SourceManagerTest.cpp - StreamCallbackTest.cpp UniqueCStringMapTest.cpp LINK_LIBS Index: lldb/unittests/Core/StreamCallbackTest.cpp =================================================================== --- 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); -} Index: lldb/unittests/Utility/LogTest.cpp =================================================================== --- lldb/unittests/Utility/LogTest.cpp +++ lldb/unittests/Utility/LogTest.cpp @@ -39,13 +39,13 @@ } // namespace lldb_private // Wrap enable, disable and list functions to make them easier to test. -static bool EnableChannel(std::shared_ptr stream_sp, +static bool EnableChannel(std::shared_ptr log_handler_sp, uint32_t log_options, llvm::StringRef channel, llvm::ArrayRef 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); } @@ -78,18 +78,28 @@ } }; +class TestLogHandler : public LogHandler { +public: + TestLogHandler() : m_messages(), m_stream(m_messages) {} + + void Emit(llvm::StringRef message) override { m_stream << message; } + void Flush() override {} + + 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 m_stream_sp = - std::make_shared(m_messages); + std::shared_ptr m_log_handler_sp = + std::make_shared(); Log *m_log; size_t m_consumed_bytes = 0; protected: - std::shared_ptr getStream() { return m_stream_sp; } + std::shared_ptr getLogHandler() { return m_log_handler_sp; } Log *getLog() { return m_log; } llvm::StringRef takeOutput(); llvm::StringRef logAndTakeOutput(llvm::StringRef Message); @@ -103,14 +113,15 @@ 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); + llvm::StringRef result = + m_log_handler_sp->m_stream.str().drop_front(m_consumed_bytes); m_consumed_bytes+= result.size(); return result; } @@ -138,9 +149,9 @@ Log::Register("chan", test_channel); EXPECT_EQ(nullptr, GetLog(TestChannel::FOO)); std::string message; - std::shared_ptr 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(); + 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)); @@ -149,22 +160,21 @@ TEST_F(LogChannelTest, Enable) { EXPECT_EQ(nullptr, GetLog(TestChannel::FOO)); std::string message; - std::shared_ptr stream_sp( - new llvm::raw_string_ostream(message)); + auto log_handler_sp = std::make_shared(); 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 +182,10 @@ TEST_F(LogChannelTest, EnableOptions) { EXPECT_EQ(nullptr, GetLog(TestChannel::FOO)); std::string message; - std::shared_ptr stream_sp( - new llvm::raw_string_ostream(message)); + auto log_handler_sp = std::make_shared(); 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 +195,9 @@ TEST_F(LogChannelTest, Disable) { EXPECT_EQ(nullptr, GetLog(TestChannel::FOO)); std::string message; - std::shared_ptr stream_sp( - new llvm::raw_string_ostream(message)); + auto log_handler_sp = std::make_shared(); 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 +234,12 @@ 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 +247,21 @@ } { - 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); 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 +310,7 @@ // (with different 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