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/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 // Wrap enable, disable and list functions to make them easier to test.
EnableChannel(std::shared_ptr<llvm::raw_ostream> stream_sp,uint32_t log_options,llvm::StringRef channel,llvm::ArrayRef<const char * > categories,std::string & error)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,
33 std::string &error) {
34 error.clear();
35 llvm::raw_string_ostream error_stream(error);
36 return Log::EnableLogChannel(stream_sp, log_options, channel, categories,
37 error_stream);
38 }
39
DisableChannel(llvm::StringRef channel,llvm::ArrayRef<const char * > categories,std::string & error)40 static bool DisableChannel(llvm::StringRef channel,
41 llvm::ArrayRef<const char *> categories,
42 std::string &error) {
43 error.clear();
44 llvm::raw_string_ostream error_stream(error);
45 return Log::DisableLogChannel(channel, categories, error_stream);
46 }
47
ListCategories(llvm::StringRef channel,std::string & result)48 static bool ListCategories(llvm::StringRef channel, std::string &result) {
49 result.clear();
50 llvm::raw_string_ostream result_stream(result);
51 return Log::ListChannelCategories(channel, result_stream);
52 }
53
54 namespace {
55 // A test fixture which provides tests with a pre-registered channel.
56 struct LogChannelTest : public ::testing::Test {
TearDown__anonabc7362e0211::LogChannelTest57 void TearDown() override { Log::DisableAllLogChannels(); }
58
SetUpTestCase__anonabc7362e0211::LogChannelTest59 static void SetUpTestCase() {
60 Log::Register("chan", test_channel);
61 }
62
TearDownTestCase__anonabc7362e0211::LogChannelTest63 static void TearDownTestCase() {
64 Log::Unregister("chan");
65 llvm::llvm_shutdown();
66 }
67 };
68
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);
76 Log *m_log;
77 size_t m_consumed_bytes = 0;
78
79 protected:
getStream()80 std::shared_ptr<llvm::raw_ostream> getStream() { return m_stream_sp; }
getLog()81 Log *getLog() { return m_log; }
82 llvm::StringRef takeOutput();
83 llvm::StringRef logAndTakeOutput(llvm::StringRef Message);
84
85 public:
86 void SetUp() override;
87 };
88 } // end anonymous namespace
89
SetUp()90 void LogChannelEnabledTest::SetUp() {
91 LogChannelTest::SetUp();
92
93 std::string error;
94 ASSERT_TRUE(EnableChannel(m_stream_sp, 0, "chan", {}, error));
95
96 m_log = test_channel.GetLogIfAll(FOO);
97 ASSERT_NE(nullptr, m_log);
98 }
99
takeOutput()100 llvm::StringRef LogChannelEnabledTest::takeOutput() {
101 llvm::StringRef result = m_stream_sp->str().drop_front(m_consumed_bytes);
102 m_consumed_bytes+= result.size();
103 return result;
104 }
105
logAndTakeOutput(llvm::StringRef Message)106 llvm::StringRef LogChannelEnabledTest::logAndTakeOutput(llvm::StringRef Message) {
107 LLDB_LOG(m_log, "{0}", Message);
108 return takeOutput();
109 }
110
TEST(LogTest,LLDB_LOG_nullptr)111 TEST(LogTest, LLDB_LOG_nullptr) {
112 Log *log = nullptr;
113 LLDB_LOG(log, "{0}", 0); // Shouldn't crash
114 }
115
TEST(LogTest,Register)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");
122 }
123
TEST(LogTest,Unregister)124 TEST(LogTest, Unregister) {
125 llvm::llvm_shutdown_obj obj;
126 Log::Register("chan", test_channel);
127 EXPECT_EQ(nullptr, test_channel.GetLogIfAny(FOO));
128 std::string message;
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));
135 }
136
TEST_F(LogChannelTest,Enable)137 TEST_F(LogChannelTest, Enable) {
138 EXPECT_EQ(nullptr, test_channel.GetLogIfAll(FOO));
139 std::string message;
140 std::shared_ptr<llvm::raw_string_ostream> stream_sp(
141 new llvm::raw_string_ostream(message));
142 std::string error;
143 ASSERT_FALSE(EnableChannel(stream_sp, 0, "chanchan", {}, error));
144 EXPECT_EQ("Invalid log channel 'chanchan'.\n", error);
145
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));
149
150 EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {"bar"}, error));
151 EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO | BAR));
152
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));
157 }
158
TEST_F(LogChannelTest,EnableOptions)159 TEST_F(LogChannelTest, EnableOptions) {
160 EXPECT_EQ(nullptr, test_channel.GetLogIfAll(FOO));
161 std::string message;
162 std::shared_ptr<llvm::raw_string_ostream> stream_sp(
163 new llvm::raw_string_ostream(message));
164 std::string error;
165 EXPECT_TRUE(
166 EnableChannel(stream_sp, LLDB_LOG_OPTION_VERBOSE, "chan", {}, error));
167
168 Log *log = test_channel.GetLogIfAll(FOO);
169 ASSERT_NE(nullptr, log);
170 EXPECT_TRUE(log->GetVerbose());
171 }
172
TEST_F(LogChannelTest,Disable)173 TEST_F(LogChannelTest, Disable) {
174 EXPECT_EQ(nullptr, test_channel.GetLogIfAll(FOO));
175 std::string message;
176 std::shared_ptr<llvm::raw_string_ostream> stream_sp(
177 new llvm::raw_string_ostream(message));
178 std::string error;
179 EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {"foo", "bar"}, error));
180 EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO | BAR));
181
182 EXPECT_TRUE(DisableChannel("chan", {"bar"}, error));
183 EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO));
184 EXPECT_EQ(nullptr, test_channel.GetLogIfAll(BAR));
185
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));
191
192 EXPECT_TRUE(DisableChannel("chan", {}, error));
193 EXPECT_EQ(nullptr, test_channel.GetLogIfAny(FOO | BAR));
194 }
195
TEST_F(LogChannelTest,List)196 TEST_F(LogChannelTest, List) {
197 std::string 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
203 foo - log foo
204 bar - log bar
205 )";
206 EXPECT_EQ(expected, list);
207
208 EXPECT_FALSE(ListCategories("chanchan", list));
209 EXPECT_EQ("Invalid log channel 'chanchan'.\n", list);
210 }
211
TEST_F(LogChannelEnabledTest,log_options)212 TEST_F(LogChannelEnabledTest, log_options) {
213 std::string Err;
214 EXPECT_EQ("Hello World\n", logAndTakeOutput("Hello World"));
215 EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_THREADSAFE, "chan", {},
216 Err));
217 EXPECT_EQ("Hello World\n", logAndTakeOutput("Hello World"));
218
219 {
220 EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_PREPEND_SEQUENCE,
221 "chan", {}, Err));
222 llvm::StringRef Msg = logAndTakeOutput("Hello World");
223 int seq_no;
224 EXPECT_EQ(1, sscanf(Msg.str().c_str(), "%d Hello World", &seq_no));
225 }
226
227 {
228 EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION,
229 "chan", {}, Err));
230 llvm::StringRef Msg = logAndTakeOutput("Hello World");
231 char File[12];
232 char Function[17];
233
234 sscanf(Msg.str().c_str(), "%[^:]:%s Hello World", File, Function);
235 EXPECT_STRCASEEQ("LogTest.cpp", File);
236 EXPECT_STREQ("logAndTakeOutput", Function);
237 }
238
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())
243 .str(),
244 logAndTakeOutput("Hello World"));
245 }
246
TEST_F(LogChannelEnabledTest,LLDB_LOG_ERROR)247 TEST_F(LogChannelEnabledTest, LLDB_LOG_ERROR) {
248 LLDB_LOG_ERROR(getLog(), llvm::Error::success(), "Foo failed: {0}");
249 ASSERT_EQ("", takeOutput());
250
251 LLDB_LOG_ERROR(getLog(),
252 llvm::make_error<llvm::StringError>(
253 "My Error", llvm::inconvertibleErrorCode()),
254 "Foo failed: {0}");
255 ASSERT_EQ("Foo failed: My Error\n", takeOutput());
256
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()),
261 "Foo failed: {0}");
262 }
263
TEST_F(LogChannelEnabledTest,LogThread)264 TEST_F(LogChannelEnabledTest, LogThread) {
265 // Test that we are able to concurrently write to a log channel and disable
266 // it.
267 std::string err;
268
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));
272 log_thread.join();
273
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"));
278 }
279
TEST_F(LogChannelEnabledTest,LogVerboseThread)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.
283 std::string err;
284
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"); });
288 EXPECT_TRUE(
289 EnableChannel(getStream(), LLDB_LOG_OPTION_VERBOSE, "chan", {}, err));
290 log_thread.join();
291
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
294 // verify this).
295 EXPECT_THAT(takeOutput(), testing::AnyOf("", "Hello World\n"));
296 }
297
TEST_F(LogChannelEnabledTest,LogGetLogThread)298 TEST_F(LogChannelEnabledTest, LogGetLogThread) {
299 // Test that we are able to concurrently get mask of a Log object and disable
300 // it.
301 std::string err;
302
303 // Try fetching the log mask on one thread. Concurrently, try disabling the
304 // log channel.
305 uint32_t mask;
306 std::thread log_thread([this, &mask] { mask = getLog()->GetMask().Get(); });
307 EXPECT_TRUE(DisableChannel("chan", {}, err));
308 log_thread.join();
309
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));
313 }
314