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