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