1 //===-- LogTest.cpp ---------------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "gtest/gtest.h" 11 12 #include "lldb/Utility/Log.h" 13 #include "lldb/Utility/StreamString.h" 14 #include "llvm/Support/ManagedStatic.h" 15 #include "llvm/Support/Threading.h" 16 #include <thread> 17 18 using namespace lldb; 19 using namespace lldb_private; 20 21 enum { FOO = 1, BAR = 2 }; 22 static constexpr Log::Category test_categories[] = { 23 {{"foo"}, {"log foo"}, FOO}, {{"bar"}, {"log bar"}, BAR}, 24 }; 25 static constexpr uint32_t default_flags = FOO; 26 27 static Log::Channel test_channel(test_categories, default_flags); 28 29 struct LogChannelTest : public ::testing::Test { 30 void TearDown() override { Log::DisableAllLogChannels(); } 31 32 static void SetUpTestCase() { 33 Log::Register("chan", test_channel); 34 } 35 36 static void TearDownTestCase() { 37 Log::Unregister("chan"); 38 llvm::llvm_shutdown(); 39 } 40 }; 41 42 // Wrap enable, disable and list functions to make them easier to test. 43 static bool EnableChannel(std::shared_ptr<llvm::raw_ostream> stream_sp, 44 uint32_t log_options, llvm::StringRef channel, 45 llvm::ArrayRef<const char *> categories, 46 std::string &error) { 47 error.clear(); 48 llvm::raw_string_ostream error_stream(error); 49 return Log::EnableLogChannel(stream_sp, log_options, channel, categories, 50 error_stream); 51 } 52 53 static bool DisableChannel(llvm::StringRef channel, 54 llvm::ArrayRef<const char *> categories, 55 std::string &error) { 56 error.clear(); 57 llvm::raw_string_ostream error_stream(error); 58 return Log::DisableLogChannel(channel, categories, error_stream); 59 } 60 61 static bool ListCategories(llvm::StringRef channel, std::string &result) { 62 result.clear(); 63 llvm::raw_string_ostream result_stream(result); 64 return Log::ListChannelCategories(channel, result_stream); 65 } 66 67 TEST(LogTest, LLDB_LOG_nullptr) { 68 Log *log = nullptr; 69 LLDB_LOG(log, "{0}", 0); // Shouldn't crash 70 } 71 72 TEST(LogTest, Register) { 73 llvm::llvm_shutdown_obj obj; 74 Log::Register("chan", test_channel); 75 Log::Unregister("chan"); 76 Log::Register("chan", test_channel); 77 Log::Unregister("chan"); 78 } 79 80 TEST(LogTest, Unregister) { 81 llvm::llvm_shutdown_obj obj; 82 Log::Register("chan", test_channel); 83 EXPECT_EQ(nullptr, test_channel.GetLogIfAny(FOO)); 84 std::string message; 85 std::shared_ptr<llvm::raw_string_ostream> stream_sp( 86 new llvm::raw_string_ostream(message)); 87 EXPECT_TRUE(Log::EnableLogChannel(stream_sp, 0, "chan", {"foo"}, llvm::nulls())); 88 EXPECT_NE(nullptr, test_channel.GetLogIfAny(FOO)); 89 Log::Unregister("chan"); 90 EXPECT_EQ(nullptr, test_channel.GetLogIfAny(FOO)); 91 } 92 93 TEST_F(LogChannelTest, Enable) { 94 EXPECT_EQ(nullptr, test_channel.GetLogIfAll(FOO)); 95 std::string message; 96 std::shared_ptr<llvm::raw_string_ostream> stream_sp( 97 new llvm::raw_string_ostream(message)); 98 std::string error; 99 ASSERT_FALSE(EnableChannel(stream_sp, 0, "chanchan", {}, error)); 100 EXPECT_EQ("Invalid log channel 'chanchan'.\n", error); 101 102 EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {}, error)); 103 EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO)); 104 EXPECT_EQ(nullptr, test_channel.GetLogIfAll(BAR)); 105 106 EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {"bar"}, error)); 107 EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO | BAR)); 108 109 EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {"baz"}, error)); 110 EXPECT_NE(std::string::npos, error.find("unrecognized log category 'baz'")) 111 << "error: " << error; 112 EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO | BAR)); 113 } 114 115 TEST_F(LogChannelTest, EnableOptions) { 116 EXPECT_EQ(nullptr, test_channel.GetLogIfAll(FOO)); 117 std::string message; 118 std::shared_ptr<llvm::raw_string_ostream> stream_sp( 119 new llvm::raw_string_ostream(message)); 120 std::string error; 121 EXPECT_TRUE( 122 EnableChannel(stream_sp, LLDB_LOG_OPTION_VERBOSE, "chan", {}, error)); 123 124 Log *log = test_channel.GetLogIfAll(FOO); 125 ASSERT_NE(nullptr, log); 126 EXPECT_TRUE(log->GetVerbose()); 127 } 128 129 TEST_F(LogChannelTest, Disable) { 130 EXPECT_EQ(nullptr, test_channel.GetLogIfAll(FOO)); 131 std::string message; 132 std::shared_ptr<llvm::raw_string_ostream> stream_sp( 133 new llvm::raw_string_ostream(message)); 134 std::string error; 135 EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {"foo", "bar"}, error)); 136 EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO | BAR)); 137 138 EXPECT_TRUE(DisableChannel("chan", {"bar"}, error)); 139 EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO)); 140 EXPECT_EQ(nullptr, test_channel.GetLogIfAll(BAR)); 141 142 EXPECT_TRUE(DisableChannel("chan", {"baz"}, error)); 143 EXPECT_NE(std::string::npos, error.find("unrecognized log category 'baz'")) 144 << "error: " << error; 145 EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO)); 146 EXPECT_EQ(nullptr, test_channel.GetLogIfAll(BAR)); 147 148 EXPECT_TRUE(DisableChannel("chan", {}, error)); 149 EXPECT_EQ(nullptr, test_channel.GetLogIfAny(FOO | BAR)); 150 } 151 152 TEST_F(LogChannelTest, List) { 153 std::string list; 154 EXPECT_TRUE(ListCategories("chan", list)); 155 std::string expected = 156 R"(Logging categories for 'chan': 157 all - all available logging categories 158 default - default set of logging categories 159 foo - log foo 160 bar - log bar 161 )"; 162 EXPECT_EQ(expected, list); 163 164 EXPECT_FALSE(ListCategories("chanchan", list)); 165 EXPECT_EQ("Invalid log channel 'chanchan'.\n", list); 166 } 167 168 static std::string GetLogString(uint32_t log_options, const char *format, 169 int arg) { 170 std::string message; 171 std::shared_ptr<llvm::raw_string_ostream> stream_sp( 172 new llvm::raw_string_ostream(message)); 173 std::string error; 174 llvm::raw_string_ostream error_stream(error); 175 EXPECT_TRUE( 176 Log::EnableLogChannel(stream_sp, log_options, "chan", {}, error_stream)); 177 178 Log *log = test_channel.GetLogIfAll(FOO); 179 EXPECT_NE(nullptr, log); 180 181 LLDB_LOG(log, format, arg); 182 EXPECT_TRUE(Log::DisableLogChannel("chan", {}, error_stream)); 183 184 return stream_sp->str(); 185 } 186 187 TEST_F(LogChannelTest, log_options) { 188 EXPECT_EQ("Hello World 47\n", GetLogString(0, "Hello World {0}", 47)); 189 EXPECT_EQ("Hello World 47\n", 190 GetLogString(LLDB_LOG_OPTION_THREADSAFE, "Hello World {0}", 47)); 191 192 { 193 std::string msg = 194 GetLogString(LLDB_LOG_OPTION_PREPEND_SEQUENCE, "Hello World {0}", 47); 195 int seq_no; 196 EXPECT_EQ(1, sscanf(msg.c_str(), "%d Hello World 47", &seq_no)); 197 } 198 199 EXPECT_EQ( 200 "LogTest.cpp:GetLogString Hello " 201 "World 47\n", 202 GetLogString(LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION, "Hello World {0}", 47)); 203 204 EXPECT_EQ(llvm::formatv("[{0,0+4}/{1,0+4}] Hello World 47\n", ::getpid(), 205 llvm::get_threadid()) 206 .str(), 207 GetLogString(LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD, 208 "Hello World {0}", 47)); 209 } 210 211 TEST_F(LogChannelTest, LogThread) { 212 // Test that we are able to concurrently write to a log channel and disable 213 // it. 214 std::string message; 215 std::shared_ptr<llvm::raw_string_ostream> stream_sp( 216 new llvm::raw_string_ostream(message)); 217 std::string err; 218 EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {}, err)); 219 220 Log *log = test_channel.GetLogIfAll(FOO); 221 222 // Start logging on one thread. Concurrently, try disabling the log channel. 223 std::thread log_thread([log] { LLDB_LOG(log, "Hello World"); }); 224 EXPECT_TRUE(DisableChannel("chan", {}, err)); 225 log_thread.join(); 226 227 // The log thread either managed to write to the log in time, or it didn't. In 228 // either case, we should not trip any undefined behavior (run the test under 229 // TSAN to verify this). 230 EXPECT_TRUE(stream_sp->str() == "" || stream_sp->str() == "Hello World\n") 231 << "str(): " << stream_sp->str(); 232 } 233 234 TEST_F(LogChannelTest, LogVerboseThread) { 235 // Test that we are able to concurrently check the verbose flag of a log 236 // channel and enable it. 237 std::string message; 238 std::shared_ptr<llvm::raw_string_ostream> stream_sp( 239 new llvm::raw_string_ostream(message)); 240 std::string err; 241 EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {}, err)); 242 243 Log *log = test_channel.GetLogIfAll(FOO); 244 245 // Start logging on one thread. Concurrently, try enabling the log channel 246 // (with different log options). 247 std::thread log_thread([log] { LLDB_LOGV(log, "Hello World"); }); 248 EXPECT_TRUE(EnableChannel(stream_sp, LLDB_LOG_OPTION_VERBOSE, "chan", 249 {}, err)); 250 log_thread.join(); 251 EXPECT_TRUE(DisableChannel("chan", {}, err)); 252 253 // The log thread either managed to write to the log, or it didn't. In either 254 // case, we should not trip any undefined behavior (run the test under TSAN to 255 // verify this). 256 EXPECT_TRUE(stream_sp->str() == "" || stream_sp->str() == "Hello World\n") 257 << "str(): " << stream_sp->str(); 258 } 259 260 TEST_F(LogChannelTest, LogGetLogThread) { 261 // Test that we are able to concurrently get mask of a Log object and disable 262 // it. 263 std::string message; 264 std::shared_ptr<llvm::raw_string_ostream> stream_sp( 265 new llvm::raw_string_ostream(message)); 266 std::string err; 267 EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {}, err)); 268 Log *log = test_channel.GetLogIfAll(FOO); 269 270 // Try fetching the log on one thread. Concurrently, try disabling the log 271 // channel. 272 uint32_t mask; 273 std::thread log_thread([log, &mask] { mask = log->GetMask().Get(); }); 274 EXPECT_TRUE(DisableChannel("chan", {}, err)); 275 log_thread.join(); 276 277 // The mask should be either zero of "FOO". In either case, we should not trip 278 // any undefined behavior (run the test under TSAN to verify this). 279 EXPECT_TRUE(mask == 0 || mask == FOO) << "mask: " << mask; 280 } 281