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/Support/ManagedStatic.h"
15 #include "llvm/Support/Threading.h"
19 using namespace lldb_private
;
21 enum { FOO
= 1, BAR
= 2 };
22 static constexpr Log::Category test_categories
[] = {
23 {{"foo"}, {"log foo"}, FOO
}, {{"bar"}, {"log bar"}, BAR
},
25 static constexpr uint32_t default_flags
= FOO
;
27 static Log::Channel
test_channel(test_categories
, default_flags
);
29 // Wrap enable, disable and list functions to make them easier to test.
30 static bool EnableChannel(std::shared_ptr
<llvm::raw_ostream
> stream_sp
,
31 uint32_t log_options
, llvm::StringRef channel
,
32 llvm::ArrayRef
<const char *> categories
,
35 llvm::raw_string_ostream
error_stream(error
);
36 return Log::EnableLogChannel(stream_sp
, log_options
, channel
, categories
,
40 static bool DisableChannel(llvm::StringRef channel
,
41 llvm::ArrayRef
<const char *> categories
,
44 llvm::raw_string_ostream
error_stream(error
);
45 return Log::DisableLogChannel(channel
, categories
, error_stream
);
48 static bool ListCategories(llvm::StringRef channel
, std::string
&result
) {
50 llvm::raw_string_ostream
result_stream(result
);
51 return Log::ListChannelCategories(channel
, result_stream
);
55 // A test fixture which provides tests with a pre-registered channel.
56 struct LogChannelTest
: public ::testing::Test
{
57 void TearDown() override
{ Log::DisableAllLogChannels(); }
59 static void SetUpTestCase() {
60 Log::Register("chan", test_channel
);
63 static void TearDownTestCase() {
64 Log::Unregister("chan");
65 llvm::llvm_shutdown();
69 // A test fixture which provides tests with a pre-registered and pre-enabled
70 // channel. Additionally, the messages written to that channel are captured and
71 // made available via getMessage().
72 class LogChannelEnabledTest
: public LogChannelTest
{
73 llvm::SmallString
<0> m_messages
;
74 std::shared_ptr
<llvm::raw_svector_ostream
> m_stream_sp
=
75 std::make_shared
<llvm::raw_svector_ostream
>(m_messages
);
77 size_t m_consumed_bytes
= 0;
80 std::shared_ptr
<llvm::raw_ostream
> getStream() { return m_stream_sp
; }
81 Log
*getLog() { return m_log
; }
82 llvm::StringRef
takeOutput();
83 llvm::StringRef
logAndTakeOutput(llvm::StringRef Message
);
86 void SetUp() override
;
88 } // end anonymous namespace
90 void LogChannelEnabledTest::SetUp() {
91 LogChannelTest::SetUp();
94 ASSERT_TRUE(EnableChannel(m_stream_sp
, 0, "chan", {}, error
));
96 m_log
= test_channel
.GetLogIfAll(FOO
);
97 ASSERT_NE(nullptr, m_log
);
100 llvm::StringRef
LogChannelEnabledTest::takeOutput() {
101 llvm::StringRef result
= m_stream_sp
->str().drop_front(m_consumed_bytes
);
102 m_consumed_bytes
+= result
.size();
106 llvm::StringRef
LogChannelEnabledTest::logAndTakeOutput(llvm::StringRef Message
) {
107 LLDB_LOG(m_log
, "{0}", Message
);
111 TEST(LogTest
, LLDB_LOG_nullptr
) {
113 LLDB_LOG(log
, "{0}", 0); // Shouldn't crash
116 TEST(LogTest
, Register
) {
117 llvm::llvm_shutdown_obj obj
;
118 Log::Register("chan", test_channel
);
119 Log::Unregister("chan");
120 Log::Register("chan", test_channel
);
121 Log::Unregister("chan");
124 TEST(LogTest
, Unregister
) {
125 llvm::llvm_shutdown_obj obj
;
126 Log::Register("chan", test_channel
);
127 EXPECT_EQ(nullptr, test_channel
.GetLogIfAny(FOO
));
129 std::shared_ptr
<llvm::raw_string_ostream
> stream_sp(
130 new llvm::raw_string_ostream(message
));
131 EXPECT_TRUE(Log::EnableLogChannel(stream_sp
, 0, "chan", {"foo"}, llvm::nulls()));
132 EXPECT_NE(nullptr, test_channel
.GetLogIfAny(FOO
));
133 Log::Unregister("chan");
134 EXPECT_EQ(nullptr, test_channel
.GetLogIfAny(FOO
));
137 TEST_F(LogChannelTest
, Enable
) {
138 EXPECT_EQ(nullptr, test_channel
.GetLogIfAll(FOO
));
140 std::shared_ptr
<llvm::raw_string_ostream
> stream_sp(
141 new llvm::raw_string_ostream(message
));
143 ASSERT_FALSE(EnableChannel(stream_sp
, 0, "chanchan", {}, error
));
144 EXPECT_EQ("Invalid log channel 'chanchan'.\n", error
);
146 EXPECT_TRUE(EnableChannel(stream_sp
, 0, "chan", {}, error
));
147 EXPECT_NE(nullptr, test_channel
.GetLogIfAll(FOO
));
148 EXPECT_EQ(nullptr, test_channel
.GetLogIfAll(BAR
));
150 EXPECT_TRUE(EnableChannel(stream_sp
, 0, "chan", {"bar"}, error
));
151 EXPECT_NE(nullptr, test_channel
.GetLogIfAll(FOO
| BAR
));
153 EXPECT_TRUE(EnableChannel(stream_sp
, 0, "chan", {"baz"}, error
));
154 EXPECT_NE(std::string::npos
, error
.find("unrecognized log category 'baz'"))
155 << "error: " << error
;
156 EXPECT_NE(nullptr, test_channel
.GetLogIfAll(FOO
| BAR
));
159 TEST_F(LogChannelTest
, EnableOptions
) {
160 EXPECT_EQ(nullptr, test_channel
.GetLogIfAll(FOO
));
162 std::shared_ptr
<llvm::raw_string_ostream
> stream_sp(
163 new llvm::raw_string_ostream(message
));
166 EnableChannel(stream_sp
, LLDB_LOG_OPTION_VERBOSE
, "chan", {}, error
));
168 Log
*log
= test_channel
.GetLogIfAll(FOO
);
169 ASSERT_NE(nullptr, log
);
170 EXPECT_TRUE(log
->GetVerbose());
173 TEST_F(LogChannelTest
, Disable
) {
174 EXPECT_EQ(nullptr, test_channel
.GetLogIfAll(FOO
));
176 std::shared_ptr
<llvm::raw_string_ostream
> stream_sp(
177 new llvm::raw_string_ostream(message
));
179 EXPECT_TRUE(EnableChannel(stream_sp
, 0, "chan", {"foo", "bar"}, error
));
180 EXPECT_NE(nullptr, test_channel
.GetLogIfAll(FOO
| BAR
));
182 EXPECT_TRUE(DisableChannel("chan", {"bar"}, error
));
183 EXPECT_NE(nullptr, test_channel
.GetLogIfAll(FOO
));
184 EXPECT_EQ(nullptr, test_channel
.GetLogIfAll(BAR
));
186 EXPECT_TRUE(DisableChannel("chan", {"baz"}, error
));
187 EXPECT_NE(std::string::npos
, error
.find("unrecognized log category 'baz'"))
188 << "error: " << error
;
189 EXPECT_NE(nullptr, test_channel
.GetLogIfAll(FOO
));
190 EXPECT_EQ(nullptr, test_channel
.GetLogIfAll(BAR
));
192 EXPECT_TRUE(DisableChannel("chan", {}, error
));
193 EXPECT_EQ(nullptr, test_channel
.GetLogIfAny(FOO
| BAR
));
196 TEST_F(LogChannelTest
, List
) {
198 EXPECT_TRUE(ListCategories("chan", list
));
199 std::string expected
=
200 R
"(Logging categories for 'chan':
201 all - all available logging categories
202 default - default set of logging categories
206 EXPECT_EQ(expected
, list
);
208 EXPECT_FALSE(ListCategories("chanchan", list
));
209 EXPECT_EQ("Invalid log channel 'chanchan'.\n", list
);
212 TEST_F(LogChannelEnabledTest
, log_options
) {
214 EXPECT_EQ("Hello World\n", logAndTakeOutput("Hello World"));
215 EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_THREADSAFE
, "chan", {},
217 EXPECT_EQ("Hello World\n", logAndTakeOutput("Hello World"));
220 EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_PREPEND_SEQUENCE
,
222 llvm::StringRef Msg
= logAndTakeOutput("Hello World");
224 EXPECT_EQ(1, sscanf(Msg
.str().c_str(), "%d Hello World", &seq_no
));
228 EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION
,
230 llvm::StringRef Msg
= logAndTakeOutput("Hello World");
234 sscanf(Msg
.str().c_str(), "%[^:]:%s Hello World", File
, Function
);
235 EXPECT_STRCASEEQ("LogTest.cpp", File
);
236 EXPECT_STREQ("logAndTakeOutput", Function
);
239 EXPECT_TRUE(EnableChannel(
240 getStream(), LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD
, "chan", {}, Err
));
241 EXPECT_EQ(llvm::formatv("[{0,0+4}/{1,0+4}] Hello World\n", ::getpid(),
242 llvm::get_threadid())
244 logAndTakeOutput("Hello World"));
247 TEST_F(LogChannelEnabledTest
, LLDB_LOG_ERROR
) {
248 LLDB_LOG_ERROR(getLog(), llvm::Error::success(), "Foo failed: {0}");
249 ASSERT_EQ("", takeOutput());
251 LLDB_LOG_ERROR(getLog(),
252 llvm::make_error
<llvm::StringError
>(
253 "My Error", llvm::inconvertibleErrorCode()),
255 ASSERT_EQ("Foo failed: My Error\n", takeOutput());
257 // Doesn't log, but doesn't assert either
258 LLDB_LOG_ERROR(nullptr,
259 llvm::make_error
<llvm::StringError
>(
260 "My Error", llvm::inconvertibleErrorCode()),
264 TEST_F(LogChannelEnabledTest
, LogThread
) {
265 // Test that we are able to concurrently write to a log channel and disable
269 // Start logging on one thread. Concurrently, try disabling the log channel.
270 std::thread
log_thread([this] { LLDB_LOG(getLog(), "Hello World"); });
271 EXPECT_TRUE(DisableChannel("chan", {}, err
));
274 // The log thread either managed to write to the log in time, or it didn't. In
275 // either case, we should not trip any undefined behavior (run the test under
276 // TSAN to verify this).
277 EXPECT_THAT(takeOutput(), testing::AnyOf("", "Hello World\n"));
280 TEST_F(LogChannelEnabledTest
, LogVerboseThread
) {
281 // Test that we are able to concurrently check the verbose flag of a log
282 // channel and enable it.
285 // Start logging on one thread. Concurrently, try enabling the log channel
286 // (with different log options).
287 std::thread
log_thread([this] { LLDB_LOGV(getLog(), "Hello World"); });
289 EnableChannel(getStream(), LLDB_LOG_OPTION_VERBOSE
, "chan", {}, err
));
292 // The log thread either managed to write to the log, or it didn't. In either
293 // case, we should not trip any undefined behavior (run the test under TSAN to
295 EXPECT_THAT(takeOutput(), testing::AnyOf("", "Hello World\n"));
298 TEST_F(LogChannelEnabledTest
, LogGetLogThread
) {
299 // Test that we are able to concurrently get mask of a Log object and disable
303 // Try fetching the log mask on one thread. Concurrently, try disabling the
306 std::thread
log_thread([this, &mask
] { mask
= getLog()->GetMask().Get(); });
307 EXPECT_TRUE(DisableChannel("chan", {}, err
));
310 // The mask should be either zero of "FOO". In either case, we should not trip
311 // any undefined behavior (run the test under TSAN to verify this).
312 EXPECT_THAT(mask
, testing::AnyOf(0, FOO
));