diff --git a/lldb/include/lldb/Utility/Log.h b/lldb/include/lldb/Utility/Log.h --- a/lldb/include/lldb/Utility/Log.h +++ b/lldb/include/lldb/Utility/Log.h @@ -82,6 +82,26 @@ void *m_baton; }; +class RotatingLogHandler : public LogHandler { +public: + RotatingLogHandler(size_t size); + + void Emit(llvm::StringRef message) override; + void Dump(llvm::raw_ostream &stream) const; + + static std::shared_ptr Create(size_t size); + +private: + size_t NormalizeIndex(size_t i) const; + size_t GetNumMessages() const; + size_t GetFirstMessageIndex() const; + + std::unique_ptr m_messages; + const size_t m_size = 0; + size_t m_next_index = 0; + size_t m_total_count = 0; +}; + class Log final { public: /// The underlying type of all log channel enums. Declare them as: diff --git a/lldb/source/Utility/Log.cpp b/lldb/source/Utility/Log.cpp --- a/lldb/source/Utility/Log.cpp +++ b/lldb/source/Utility/Log.cpp @@ -365,3 +365,37 @@ CallbackLogHandler::Create(lldb::LogOutputCallback callback, void *baton) { return std::make_shared(callback, baton); } + +RotatingLogHandler::RotatingLogHandler(size_t size) + : m_messages(std::make_unique(size)), m_size(size) {} + +void RotatingLogHandler::Emit(llvm::StringRef message) { + ++m_total_count; + const size_t index = m_next_index; + m_next_index = NormalizeIndex(index + 1); + m_messages[index] = message.str(); +} + +size_t RotatingLogHandler::NormalizeIndex(size_t i) const { return i % m_size; } + +size_t RotatingLogHandler::GetNumMessages() const { + return m_total_count < m_size ? m_total_count : m_size; +} + +size_t RotatingLogHandler::GetFirstMessageIndex() const { + return m_total_count < m_size ? 0 : m_next_index; +} + +void RotatingLogHandler::Dump(llvm::raw_ostream &stream) const { + const size_t start_idx = GetFirstMessageIndex(); + const size_t stop_idx = start_idx + GetNumMessages(); + for (size_t i = start_idx; i < stop_idx; ++i) { + const size_t idx = NormalizeIndex(i); + stream << m_messages[idx]; + } + stream.flush(); +} + +std::shared_ptr RotatingLogHandler::Create(size_t size) { + return std::make_shared(size); +} diff --git a/lldb/unittests/Utility/LogTest.cpp b/lldb/unittests/Utility/LogTest.cpp --- a/lldb/unittests/Utility/LogTest.cpp +++ b/lldb/unittests/Utility/LogTest.cpp @@ -104,6 +104,13 @@ public: void SetUp() override; }; + +static std::string GetDumpAsString(const RotatingLogHandler &handler) { + std::string buffer; + llvm::raw_string_ostream stream(buffer); + handler.Dump(stream); + return buffer; +} } // end anonymous namespace void LogChannelEnabledTest::SetUp() { @@ -171,6 +178,21 @@ EXPECT_EQ(1u, callback_count); } +TEST(LogHandlerTest, RotatingLogHandler) { + RotatingLogHandler handler(3); + + handler.Emit("foo"); + handler.Emit("bar"); + EXPECT_EQ(GetDumpAsString(handler), "foobar"); + + handler.Emit("baz"); + handler.Emit("qux"); + EXPECT_EQ(GetDumpAsString(handler), "barbazqux"); + + handler.Emit("quux"); + EXPECT_EQ(GetDumpAsString(handler), "bazquxquux"); +} + TEST_F(LogChannelTest, Enable) { EXPECT_EQ(nullptr, GetLog(TestChannel::FOO)); std::string message;