1 //===-- LogTest.cpp -------------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "gmock/gmock.h"
10 #include "gtest/gtest.h"
12 #include "lldb/Utility/Log.h"
13 #include "lldb/Utility/StreamString.h"
14 #include "llvm/ADT/BitmaskEnum.h"
15 #include "llvm/Support/ManagedStatic.h"
16 #include "llvm/Support/Threading.h"
20 using namespace lldb_private
;
22 enum class TestChannel
: Log::MaskType
{
23 FOO
= Log::ChannelFlag
<0>,
24 BAR
= Log::ChannelFlag
<1>,
25 LLVM_MARK_AS_BITMASK_ENUM(BAR
),
28 LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
30 static constexpr Log::Category test_categories
[] = {
31 {{"foo"}, {"log foo"}, TestChannel::FOO
},
32 {{"bar"}, {"log bar"}, TestChannel::BAR
},
35 static Log::Channel
test_channel(test_categories
, TestChannel::FOO
);
37 namespace lldb_private
{
38 template <> Log::Channel
&LogChannelFor
<TestChannel
>() { return test_channel
; }
39 } // namespace lldb_private
41 // Wrap enable, disable and list functions to make them easier to test.
42 static bool EnableChannel(std::shared_ptr
<LogHandler
> log_handler_sp
,
43 uint32_t log_options
, llvm::StringRef channel
,
44 llvm::ArrayRef
<const char *> categories
,
47 llvm::raw_string_ostream
error_stream(error
);
48 return Log::EnableLogChannel(log_handler_sp
, log_options
, channel
, categories
,
52 static bool DisableChannel(llvm::StringRef channel
,
53 llvm::ArrayRef
<const char *> categories
,
56 llvm::raw_string_ostream
error_stream(error
);
57 return Log::DisableLogChannel(channel
, categories
, error_stream
);
60 static bool ListCategories(llvm::StringRef channel
, std::string
&result
) {
62 llvm::raw_string_ostream
result_stream(result
);
63 return Log::ListChannelCategories(channel
, result_stream
);
67 // A test fixture which provides tests with a pre-registered channel.
68 struct LogChannelTest
: public ::testing::Test
{
69 void TearDown() override
{ Log::DisableAllLogChannels(); }
71 static void SetUpTestCase() { Log::Register("chan", test_channel
); }
73 static void TearDownTestCase() {
74 Log::Unregister("chan");
75 llvm::llvm_shutdown();
79 class TestLogHandler
: public LogHandler
{
81 TestLogHandler() : m_messages(), m_stream(m_messages
) {}
83 void Emit(llvm::StringRef message
) override
{ m_stream
<< message
; }
85 llvm::SmallString
<0> m_messages
;
86 llvm::raw_svector_ostream m_stream
;
89 // A test fixture which provides tests with a pre-registered and pre-enabled
90 // channel. Additionally, the messages written to that channel are captured and
91 // made available via getMessage().
92 class LogChannelEnabledTest
: public LogChannelTest
{
93 std::shared_ptr
<TestLogHandler
> m_log_handler_sp
=
94 std::make_shared
<TestLogHandler
>();
96 size_t m_consumed_bytes
= 0;
99 std::shared_ptr
<LogHandler
> getLogHandler() { return m_log_handler_sp
; }
100 Log
*getLog() { return m_log
; }
101 llvm::StringRef
takeOutput();
102 llvm::StringRef
logAndTakeOutput(llvm::StringRef Message
);
103 llvm::StringRef
logAndTakeOutputf(llvm::StringRef Message
);
106 void SetUp() override
;
109 static std::string
GetDumpAsString(const RotatingLogHandler
&handler
) {
111 llvm::raw_string_ostream
stream(buffer
);
112 handler
.Dump(stream
);
115 } // end anonymous namespace
117 void LogChannelEnabledTest::SetUp() {
118 LogChannelTest::SetUp();
121 ASSERT_TRUE(EnableChannel(m_log_handler_sp
, 0, "chan", {}, error
));
123 m_log
= GetLog(TestChannel::FOO
);
124 ASSERT_NE(nullptr, m_log
);
127 llvm::StringRef
LogChannelEnabledTest::takeOutput() {
128 llvm::StringRef result
=
129 m_log_handler_sp
->m_stream
.str().drop_front(m_consumed_bytes
);
130 m_consumed_bytes
+= result
.size();
135 LogChannelEnabledTest::logAndTakeOutput(llvm::StringRef Message
) {
136 LLDB_LOG(m_log
, "{0}", Message
);
141 LogChannelEnabledTest::logAndTakeOutputf(llvm::StringRef Message
) {
142 LLDB_LOGF(m_log
, "%s", Message
.str().c_str());
146 TEST(LogTest
, LLDB_LOG_nullptr
) {
148 LLDB_LOG(log
, "{0}", 0); // Shouldn't crash
151 TEST(LogTest
, Register
) {
152 llvm::llvm_shutdown_obj obj
;
153 Log::Register("chan", test_channel
);
154 Log::Unregister("chan");
155 Log::Register("chan", test_channel
);
156 Log::Unregister("chan");
159 TEST(LogTest
, Unregister
) {
160 llvm::llvm_shutdown_obj obj
;
161 Log::Register("chan", test_channel
);
162 EXPECT_EQ(nullptr, GetLog(TestChannel::FOO
));
164 auto log_handler_sp
= std::make_shared
<TestLogHandler
>();
166 Log::EnableLogChannel(log_handler_sp
, 0, "chan", {"foo"}, llvm::nulls()));
167 EXPECT_NE(nullptr, GetLog(TestChannel::FOO
));
168 Log::Unregister("chan");
169 EXPECT_EQ(nullptr, GetLog(TestChannel::FOO
));
173 static char test_baton
;
174 static size_t callback_count
= 0;
175 static void TestCallback(const char *data
, void *baton
) {
176 EXPECT_STREQ("Foobar", data
);
177 EXPECT_EQ(&test_baton
, baton
);
182 TEST(LogTest
, CallbackLogHandler
) {
183 CallbackLogHandler
handler(TestCallback
, &test_baton
);
184 handler
.Emit("Foobar");
185 EXPECT_EQ(1u, callback_count
);
188 TEST(LogHandlerTest
, RotatingLogHandler
) {
189 RotatingLogHandler
handler(3);
193 EXPECT_EQ(GetDumpAsString(handler
), "foobar");
197 EXPECT_EQ(GetDumpAsString(handler
), "barbazqux");
199 handler
.Emit("quux");
200 EXPECT_EQ(GetDumpAsString(handler
), "bazquxquux");
203 TEST(LogHandlerTest
, TeeLogHandler
) {
204 auto handler1
= std::make_shared
<RotatingLogHandler
>(2);
205 auto handler2
= std::make_shared
<RotatingLogHandler
>(2);
206 TeeLogHandler
handler(handler1
, handler2
);
211 EXPECT_EQ(GetDumpAsString(*handler1
), "foobar");
212 EXPECT_EQ(GetDumpAsString(*handler2
), "foobar");
215 TEST_F(LogChannelTest
, Enable
) {
216 EXPECT_EQ(nullptr, GetLog(TestChannel::FOO
));
218 auto log_handler_sp
= std::make_shared
<TestLogHandler
>();
220 ASSERT_FALSE(EnableChannel(log_handler_sp
, 0, "chanchan", {}, error
));
221 EXPECT_EQ("Invalid log channel 'chanchan'.\n", error
);
223 EXPECT_TRUE(EnableChannel(log_handler_sp
, 0, "chan", {}, error
));
224 EXPECT_NE(nullptr, GetLog(TestChannel::FOO
));
225 EXPECT_EQ(nullptr, GetLog(TestChannel::BAR
));
226 EXPECT_NE(nullptr, GetLog(TestChannel::FOO
| TestChannel::BAR
));
228 EXPECT_TRUE(EnableChannel(log_handler_sp
, 0, "chan", {"bar"}, error
));
229 EXPECT_NE(nullptr, GetLog(TestChannel::FOO
));
230 EXPECT_NE(nullptr, GetLog(TestChannel::BAR
));
232 EXPECT_TRUE(EnableChannel(log_handler_sp
, 0, "chan", {"baz"}, error
));
233 EXPECT_NE(std::string::npos
, error
.find("unrecognized log category 'baz'"))
234 << "error: " << error
;
237 TEST_F(LogChannelTest
, EnableOptions
) {
238 EXPECT_EQ(nullptr, GetLog(TestChannel::FOO
));
240 auto log_handler_sp
= std::make_shared
<TestLogHandler
>();
242 EXPECT_TRUE(EnableChannel(log_handler_sp
, LLDB_LOG_OPTION_VERBOSE
, "chan", {},
245 Log
*log
= GetLog(TestChannel::FOO
);
246 ASSERT_NE(nullptr, log
);
247 EXPECT_TRUE(log
->GetVerbose());
250 TEST_F(LogChannelTest
, Disable
) {
251 EXPECT_EQ(nullptr, GetLog(TestChannel::FOO
));
253 auto log_handler_sp
= std::make_shared
<TestLogHandler
>();
255 EXPECT_TRUE(EnableChannel(log_handler_sp
, 0, "chan", {"foo", "bar"}, error
));
256 EXPECT_NE(nullptr, GetLog(TestChannel::FOO
));
257 EXPECT_NE(nullptr, GetLog(TestChannel::BAR
));
259 EXPECT_TRUE(DisableChannel("chan", {"bar"}, error
));
260 EXPECT_NE(nullptr, GetLog(TestChannel::FOO
));
261 EXPECT_EQ(nullptr, GetLog(TestChannel::BAR
));
263 EXPECT_TRUE(DisableChannel("chan", {"baz"}, error
));
264 EXPECT_NE(std::string::npos
, error
.find("unrecognized log category 'baz'"))
265 << "error: " << error
;
266 EXPECT_NE(nullptr, GetLog(TestChannel::FOO
));
267 EXPECT_EQ(nullptr, GetLog(TestChannel::BAR
));
269 EXPECT_TRUE(DisableChannel("chan", {}, error
));
270 EXPECT_EQ(nullptr, GetLog(TestChannel::FOO
| TestChannel::BAR
));
273 TEST_F(LogChannelTest
, List
) {
275 EXPECT_TRUE(ListCategories("chan", list
));
276 std::string expected
=
277 R
"(Logging categories for 'chan':
278 all - all available logging categories
279 default - default set of logging categories
283 EXPECT_EQ(expected
, list
);
285 EXPECT_FALSE(ListCategories("chanchan", list
));
286 EXPECT_EQ("Invalid log channel 'chanchan'.\n", list
);
289 TEST_F(LogChannelEnabledTest
, log_options
) {
291 EXPECT_EQ("Hello World\n", logAndTakeOutput("Hello World"));
292 EXPECT_TRUE(EnableChannel(getLogHandler(), 0, "chan", {}, Err
));
293 EXPECT_EQ("Hello World\n", logAndTakeOutput("Hello World"));
296 EXPECT_TRUE(EnableChannel(getLogHandler(), LLDB_LOG_OPTION_PREPEND_SEQUENCE
,
298 llvm::StringRef Msg
= logAndTakeOutput("Hello World");
300 EXPECT_EQ(1, sscanf(Msg
.str().c_str(), "%d Hello World", &seq_no
));
304 EXPECT_TRUE(EnableChannel(getLogHandler(),
305 LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION
, "chan", {},
307 llvm::StringRef Msg
= logAndTakeOutput("Hello World");
311 sscanf(Msg
.str().c_str(),
312 "%[^:]:%s Hello World", File
,
314 EXPECT_STRCASEEQ("LogTest.cpp", File
);
315 EXPECT_STREQ("logAndTakeOutput", Function
);
319 EXPECT_TRUE(EnableChannel(getLogHandler(),
320 LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION
, "chan", {},
322 llvm::StringRef Msg
= logAndTakeOutputf("Hello World");
326 sscanf(Msg
.str().c_str(),
327 "%[^:]:%s Hello World", File
,
329 EXPECT_STRCASEEQ("LogTest.cpp", File
);
330 EXPECT_STREQ("logAndTakeOutputf", Function
);
333 EXPECT_TRUE(EnableChannel(getLogHandler(),
334 LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD
, "chan", {},
336 EXPECT_EQ(llvm::formatv("[{0,0+4}/{1,0+4}] Hello World\n", ::getpid(),
337 llvm::get_threadid())
339 logAndTakeOutput("Hello World"));
342 TEST_F(LogChannelEnabledTest
, LLDB_LOG_ERROR
) {
343 LLDB_LOG_ERROR(getLog(), llvm::Error::success(), "Foo failed: {0}");
344 ASSERT_EQ("", takeOutput());
346 LLDB_LOG_ERROR(getLog(),
347 llvm::make_error
<llvm::StringError
>(
348 "My Error", llvm::inconvertibleErrorCode()),
350 ASSERT_EQ("Foo failed: My Error\n", takeOutput());
352 // Doesn't log, but doesn't assert either
353 LLDB_LOG_ERROR(nullptr,
354 llvm::make_error
<llvm::StringError
>(
355 "My Error", llvm::inconvertibleErrorCode()),
359 TEST_F(LogChannelEnabledTest
, LogThread
) {
360 // Test that we are able to concurrently write to a log channel and disable
364 // Start logging on one thread. Concurrently, try disabling the log channel.
365 std::thread
log_thread([this] { LLDB_LOG(getLog(), "Hello World"); });
366 EXPECT_TRUE(DisableChannel("chan", {}, err
));
369 // The log thread either managed to write to the log in time, or it didn't. In
370 // either case, we should not trip any undefined behavior (run the test under
371 // TSAN to verify this).
372 EXPECT_THAT(takeOutput(), testing::AnyOf("", "Hello World\n"));
375 TEST_F(LogChannelEnabledTest
, LogVerboseThread
) {
376 // Test that we are able to concurrently check the verbose flag of a log
377 // channel and enable it.
380 // Start logging on one thread. Concurrently, try enabling the log channel
381 // (with different log options).
382 std::thread
log_thread([this] { LLDB_LOGV(getLog(), "Hello World"); });
384 EnableChannel(getLogHandler(), LLDB_LOG_OPTION_VERBOSE
, "chan", {}, err
));
387 // The log thread either managed to write to the log, or it didn't. In either
388 // case, we should not trip any undefined behavior (run the test under TSAN to
390 EXPECT_THAT(takeOutput(), testing::AnyOf("", "Hello World\n"));
393 TEST_F(LogChannelEnabledTest
, LogGetLogThread
) {
394 // Test that we are able to concurrently get mask of a Log object and disable
398 // Try fetching the log mask on one thread. Concurrently, try disabling the
401 std::thread
log_thread([this, &mask
] { mask
= getLog()->GetMask(); });
402 EXPECT_TRUE(DisableChannel("chan", {}, err
));
405 // The mask should be either zero of "FOO". In either case, we should not trip
406 // any undefined behavior (run the test under TSAN to verify this).
407 EXPECT_THAT(mask
, testing::AnyOf(0, Log::MaskType(TestChannel::FOO
)));