[MemProf] Templatize CallStackRadixTreeBuilder (NFC) (#117014)
[llvm-project.git] / lldb / unittests / Utility / LogTest.cpp
blobb9b0af4133da92638dd66148b4b144db1586170c
1 //===-- LogTest.cpp -------------------------------------------------------===//
2 //
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
6 //
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"
17 #include <thread>
19 using namespace lldb;
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,
45 std::string &error) {
46 error.clear();
47 llvm::raw_string_ostream error_stream(error);
48 return Log::EnableLogChannel(log_handler_sp, log_options, channel, categories,
49 error_stream);
52 static bool DisableChannel(llvm::StringRef channel,
53 llvm::ArrayRef<const char *> categories,
54 std::string &error) {
55 error.clear();
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) {
61 result.clear();
62 llvm::raw_string_ostream result_stream(result);
63 return Log::ListChannelCategories(channel, result_stream);
66 namespace {
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 {
80 public:
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>();
95 Log *m_log;
96 size_t m_consumed_bytes = 0;
98 protected:
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);
105 public:
106 void SetUp() override;
109 static std::string GetDumpAsString(const RotatingLogHandler &handler) {
110 std::string buffer;
111 llvm::raw_string_ostream stream(buffer);
112 handler.Dump(stream);
113 return buffer;
115 } // end anonymous namespace
117 void LogChannelEnabledTest::SetUp() {
118 LogChannelTest::SetUp();
120 std::string error;
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();
131 return result;
134 llvm::StringRef
135 LogChannelEnabledTest::logAndTakeOutput(llvm::StringRef Message) {
136 LLDB_LOG(m_log, "{0}", Message);
137 return takeOutput();
140 llvm::StringRef
141 LogChannelEnabledTest::logAndTakeOutputf(llvm::StringRef Message) {
142 LLDB_LOGF(m_log, "%s", Message.str().c_str());
143 return takeOutput();
146 TEST(LogTest, LLDB_LOG_nullptr) {
147 Log *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));
163 std::string message;
164 auto log_handler_sp = std::make_shared<TestLogHandler>();
165 EXPECT_TRUE(
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));
172 namespace {
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);
178 ++callback_count;
180 } // namespace
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);
191 handler.Emit("foo");
192 handler.Emit("bar");
193 EXPECT_EQ(GetDumpAsString(handler), "foobar");
195 handler.Emit("baz");
196 handler.Emit("qux");
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);
208 handler.Emit("foo");
209 handler.Emit("bar");
211 EXPECT_EQ(GetDumpAsString(*handler1), "foobar");
212 EXPECT_EQ(GetDumpAsString(*handler2), "foobar");
215 TEST_F(LogChannelTest, Enable) {
216 EXPECT_EQ(nullptr, GetLog(TestChannel::FOO));
217 std::string message;
218 auto log_handler_sp = std::make_shared<TestLogHandler>();
219 std::string error;
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));
239 std::string message;
240 auto log_handler_sp = std::make_shared<TestLogHandler>();
241 std::string error;
242 EXPECT_TRUE(EnableChannel(log_handler_sp, LLDB_LOG_OPTION_VERBOSE, "chan", {},
243 error));
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));
252 std::string message;
253 auto log_handler_sp = std::make_shared<TestLogHandler>();
254 std::string error;
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) {
274 std::string 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
280 foo - log foo
281 bar - log bar
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) {
290 std::string Err;
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,
297 "chan", {}, Err));
298 llvm::StringRef Msg = logAndTakeOutput("Hello World");
299 int seq_no;
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", {},
306 Err));
307 llvm::StringRef Msg = logAndTakeOutput("Hello World");
308 char File[12];
309 char Function[17];
311 sscanf(Msg.str().c_str(),
312 "%[^:]:%s Hello World", File,
313 Function);
314 EXPECT_STRCASEEQ("LogTest.cpp", File);
315 EXPECT_STREQ("logAndTakeOutput", Function);
319 EXPECT_TRUE(EnableChannel(getLogHandler(),
320 LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION, "chan", {},
321 Err));
322 llvm::StringRef Msg = logAndTakeOutputf("Hello World");
323 char File[12];
324 char Function[18];
326 sscanf(Msg.str().c_str(),
327 "%[^:]:%s Hello World", File,
328 Function);
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", {},
335 Err));
336 EXPECT_EQ(llvm::formatv("[{0,0+4}/{1,0+4}] Hello World\n", ::getpid(),
337 llvm::get_threadid())
338 .str(),
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()),
349 "Foo failed: {0}");
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()),
356 "Foo failed: {0}");
359 TEST_F(LogChannelEnabledTest, LogThread) {
360 // Test that we are able to concurrently write to a log channel and disable
361 // it.
362 std::string err;
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));
367 log_thread.join();
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.
378 std::string err;
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"); });
383 EXPECT_TRUE(
384 EnableChannel(getLogHandler(), LLDB_LOG_OPTION_VERBOSE, "chan", {}, err));
385 log_thread.join();
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
389 // verify this).
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
395 // it.
396 std::string err;
398 // Try fetching the log mask on one thread. Concurrently, try disabling the
399 // log channel.
400 uint64_t mask;
401 std::thread log_thread([this, &mask] { mask = getLog()->GetMask(); });
402 EXPECT_TRUE(DisableChannel("chan", {}, err));
403 log_thread.join();
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)));