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