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 //===----------------------------------------------------------------------===// 8 9 #include "gmock/gmock.h" 10 #include "gtest/gtest.h" 11 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> 18 19 using namespace lldb; 20 using namespace lldb_private; 21 22 enum class TestChannel : Log::MaskType { 23 FOO = Log::ChannelFlag<0>, 24 BAR = Log::ChannelFlag<1>, 25 LLVM_MARK_AS_BITMASK_ENUM(BAR), 26 }; 27 28 LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); 29 30 static constexpr Log::Category test_categories[] = { 31 {{"foo"}, {"log foo"}, TestChannel::FOO}, 32 {{"bar"}, {"log bar"}, TestChannel::BAR}, 33 }; 34 35 static Log::Channel test_channel(test_categories, TestChannel::FOO); 36 37 namespace lldb_private { 38 template <> Log::Channel &LogChannelFor<TestChannel>() { return test_channel; } 39 } // namespace lldb_private 40 41 // Wrap enable, disable and list functions to make them easier to test. 42 static bool EnableChannel(std::shared_ptr<llvm::raw_ostream> stream_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(stream_sp, log_options, channel, categories, 49 error_stream); 50 } 51 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); 58 } 59 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); 64 } 65 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(); } 70 71 static void SetUpTestCase() { 72 Log::Register("chan", test_channel); 73 } 74 75 static void TearDownTestCase() { 76 Log::Unregister("chan"); 77 llvm::llvm_shutdown(); 78 } 79 }; 80 81 // A test fixture which provides tests with a pre-registered and pre-enabled 82 // channel. Additionally, the messages written to that channel are captured and 83 // made available via getMessage(). 84 class LogChannelEnabledTest : public LogChannelTest { 85 llvm::SmallString<0> m_messages; 86 std::shared_ptr<llvm::raw_svector_ostream> m_stream_sp = 87 std::make_shared<llvm::raw_svector_ostream>(m_messages); 88 Log *m_log; 89 size_t m_consumed_bytes = 0; 90 91 protected: 92 std::shared_ptr<llvm::raw_ostream> getStream() { return m_stream_sp; } 93 Log *getLog() { return m_log; } 94 llvm::StringRef takeOutput(); 95 llvm::StringRef logAndTakeOutput(llvm::StringRef Message); 96 97 public: 98 void SetUp() override; 99 }; 100 } // end anonymous namespace 101 102 void LogChannelEnabledTest::SetUp() { 103 LogChannelTest::SetUp(); 104 105 std::string error; 106 ASSERT_TRUE(EnableChannel(m_stream_sp, 0, "chan", {}, error)); 107 108 m_log = GetLog(TestChannel::FOO); 109 ASSERT_NE(nullptr, m_log); 110 } 111 112 llvm::StringRef LogChannelEnabledTest::takeOutput() { 113 llvm::StringRef result = m_stream_sp->str().drop_front(m_consumed_bytes); 114 m_consumed_bytes+= result.size(); 115 return result; 116 } 117 118 llvm::StringRef LogChannelEnabledTest::logAndTakeOutput(llvm::StringRef Message) { 119 LLDB_LOG(m_log, "{0}", Message); 120 return takeOutput(); 121 } 122 123 TEST(LogTest, LLDB_LOG_nullptr) { 124 Log *log = nullptr; 125 LLDB_LOG(log, "{0}", 0); // Shouldn't crash 126 } 127 128 TEST(LogTest, Register) { 129 llvm::llvm_shutdown_obj obj; 130 Log::Register("chan", test_channel); 131 Log::Unregister("chan"); 132 Log::Register("chan", test_channel); 133 Log::Unregister("chan"); 134 } 135 136 TEST(LogTest, Unregister) { 137 llvm::llvm_shutdown_obj obj; 138 Log::Register("chan", test_channel); 139 EXPECT_EQ(nullptr, GetLog(TestChannel::FOO)); 140 std::string message; 141 std::shared_ptr<llvm::raw_string_ostream> stream_sp( 142 new llvm::raw_string_ostream(message)); 143 EXPECT_TRUE(Log::EnableLogChannel(stream_sp, 0, "chan", {"foo"}, llvm::nulls())); 144 EXPECT_NE(nullptr, GetLog(TestChannel::FOO)); 145 Log::Unregister("chan"); 146 EXPECT_EQ(nullptr, GetLog(TestChannel::FOO)); 147 } 148 149 TEST_F(LogChannelTest, Enable) { 150 EXPECT_EQ(nullptr, GetLog(TestChannel::FOO)); 151 std::string message; 152 std::shared_ptr<llvm::raw_string_ostream> stream_sp( 153 new llvm::raw_string_ostream(message)); 154 std::string error; 155 ASSERT_FALSE(EnableChannel(stream_sp, 0, "chanchan", {}, error)); 156 EXPECT_EQ("Invalid log channel 'chanchan'.\n", error); 157 158 EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {}, error)); 159 EXPECT_NE(nullptr, GetLog(TestChannel::FOO)); 160 EXPECT_EQ(nullptr, GetLog(TestChannel::BAR)); 161 EXPECT_NE(nullptr, GetLog(TestChannel::FOO | TestChannel::BAR)); 162 163 EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {"bar"}, error)); 164 EXPECT_NE(nullptr, GetLog(TestChannel::FOO)); 165 EXPECT_NE(nullptr, GetLog(TestChannel::BAR)); 166 167 EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {"baz"}, error)); 168 EXPECT_NE(std::string::npos, error.find("unrecognized log category 'baz'")) 169 << "error: " << error; 170 } 171 172 TEST_F(LogChannelTest, EnableOptions) { 173 EXPECT_EQ(nullptr, GetLog(TestChannel::FOO)); 174 std::string message; 175 std::shared_ptr<llvm::raw_string_ostream> stream_sp( 176 new llvm::raw_string_ostream(message)); 177 std::string error; 178 EXPECT_TRUE( 179 EnableChannel(stream_sp, LLDB_LOG_OPTION_VERBOSE, "chan", {}, error)); 180 181 Log *log = GetLog(TestChannel::FOO); 182 ASSERT_NE(nullptr, log); 183 EXPECT_TRUE(log->GetVerbose()); 184 } 185 186 TEST_F(LogChannelTest, Disable) { 187 EXPECT_EQ(nullptr, GetLog(TestChannel::FOO)); 188 std::string message; 189 std::shared_ptr<llvm::raw_string_ostream> stream_sp( 190 new llvm::raw_string_ostream(message)); 191 std::string error; 192 EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {"foo", "bar"}, error)); 193 EXPECT_NE(nullptr, GetLog(TestChannel::FOO)); 194 EXPECT_NE(nullptr, GetLog(TestChannel::BAR)); 195 196 EXPECT_TRUE(DisableChannel("chan", {"bar"}, error)); 197 EXPECT_NE(nullptr, GetLog(TestChannel::FOO)); 198 EXPECT_EQ(nullptr, GetLog(TestChannel::BAR)); 199 200 EXPECT_TRUE(DisableChannel("chan", {"baz"}, error)); 201 EXPECT_NE(std::string::npos, error.find("unrecognized log category 'baz'")) 202 << "error: " << error; 203 EXPECT_NE(nullptr, GetLog(TestChannel::FOO)); 204 EXPECT_EQ(nullptr, GetLog(TestChannel::BAR)); 205 206 EXPECT_TRUE(DisableChannel("chan", {}, error)); 207 EXPECT_EQ(nullptr, GetLog(TestChannel::FOO | TestChannel::BAR)); 208 } 209 210 TEST_F(LogChannelTest, List) { 211 std::string list; 212 EXPECT_TRUE(ListCategories("chan", list)); 213 std::string expected = 214 R"(Logging categories for 'chan': 215 all - all available logging categories 216 default - default set of logging categories 217 foo - log foo 218 bar - log bar 219 )"; 220 EXPECT_EQ(expected, list); 221 222 EXPECT_FALSE(ListCategories("chanchan", list)); 223 EXPECT_EQ("Invalid log channel 'chanchan'.\n", list); 224 } 225 226 TEST_F(LogChannelEnabledTest, log_options) { 227 std::string Err; 228 EXPECT_EQ("Hello World\n", logAndTakeOutput("Hello World")); 229 EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_THREADSAFE, "chan", {}, 230 Err)); 231 EXPECT_EQ("Hello World\n", logAndTakeOutput("Hello World")); 232 233 { 234 EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_PREPEND_SEQUENCE, 235 "chan", {}, Err)); 236 llvm::StringRef Msg = logAndTakeOutput("Hello World"); 237 int seq_no; 238 EXPECT_EQ(1, sscanf(Msg.str().c_str(), "%d Hello World", &seq_no)); 239 } 240 241 { 242 EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION, 243 "chan", {}, Err)); 244 llvm::StringRef Msg = logAndTakeOutput("Hello World"); 245 char File[12]; 246 char Function[17]; 247 248 sscanf(Msg.str().c_str(), "%[^:]:%s Hello World", File, Function); 249 EXPECT_STRCASEEQ("LogTest.cpp", File); 250 EXPECT_STREQ("logAndTakeOutput", Function); 251 } 252 253 EXPECT_TRUE(EnableChannel( 254 getStream(), LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD, "chan", {}, Err)); 255 EXPECT_EQ(llvm::formatv("[{0,0+4}/{1,0+4}] Hello World\n", ::getpid(), 256 llvm::get_threadid()) 257 .str(), 258 logAndTakeOutput("Hello World")); 259 } 260 261 TEST_F(LogChannelEnabledTest, LLDB_LOG_ERROR) { 262 LLDB_LOG_ERROR(getLog(), llvm::Error::success(), "Foo failed: {0}"); 263 ASSERT_EQ("", takeOutput()); 264 265 LLDB_LOG_ERROR(getLog(), 266 llvm::make_error<llvm::StringError>( 267 "My Error", llvm::inconvertibleErrorCode()), 268 "Foo failed: {0}"); 269 ASSERT_EQ("Foo failed: My Error\n", takeOutput()); 270 271 // Doesn't log, but doesn't assert either 272 LLDB_LOG_ERROR(nullptr, 273 llvm::make_error<llvm::StringError>( 274 "My Error", llvm::inconvertibleErrorCode()), 275 "Foo failed: {0}"); 276 } 277 278 TEST_F(LogChannelEnabledTest, LogThread) { 279 // Test that we are able to concurrently write to a log channel and disable 280 // it. 281 std::string err; 282 283 // Start logging on one thread. Concurrently, try disabling the log channel. 284 std::thread log_thread([this] { LLDB_LOG(getLog(), "Hello World"); }); 285 EXPECT_TRUE(DisableChannel("chan", {}, err)); 286 log_thread.join(); 287 288 // The log thread either managed to write to the log in time, or it didn't. In 289 // either case, we should not trip any undefined behavior (run the test under 290 // TSAN to verify this). 291 EXPECT_THAT(takeOutput(), testing::AnyOf("", "Hello World\n")); 292 } 293 294 TEST_F(LogChannelEnabledTest, LogVerboseThread) { 295 // Test that we are able to concurrently check the verbose flag of a log 296 // channel and enable it. 297 std::string err; 298 299 // Start logging on one thread. Concurrently, try enabling the log channel 300 // (with different log options). 301 std::thread log_thread([this] { LLDB_LOGV(getLog(), "Hello World"); }); 302 EXPECT_TRUE( 303 EnableChannel(getStream(), LLDB_LOG_OPTION_VERBOSE, "chan", {}, err)); 304 log_thread.join(); 305 306 // The log thread either managed to write to the log, or it didn't. In either 307 // case, we should not trip any undefined behavior (run the test under TSAN to 308 // verify this). 309 EXPECT_THAT(takeOutput(), testing::AnyOf("", "Hello World\n")); 310 } 311 312 TEST_F(LogChannelEnabledTest, LogGetLogThread) { 313 // Test that we are able to concurrently get mask of a Log object and disable 314 // it. 315 std::string err; 316 317 // Try fetching the log mask on one thread. Concurrently, try disabling the 318 // log channel. 319 uint32_t mask; 320 std::thread log_thread([this, &mask] { mask = getLog()->GetMask().Get(); }); 321 EXPECT_TRUE(DisableChannel("chan", {}, err)); 322 log_thread.join(); 323 324 // The mask should be either zero of "FOO". In either case, we should not trip 325 // any undefined behavior (run the test under TSAN to verify this). 326 EXPECT_THAT(mask, testing::AnyOf(0, Log::MaskType(TestChannel::FOO))); 327 } 328