1 //===-- Log.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 "lldb/Utility/Log.h"
10 #include "lldb/Utility/VASPrintf.h"
11
12 #include "llvm/ADT/SmallString.h"
13 #include "llvm/ADT/Twine.h"
14 #include "llvm/ADT/iterator.h"
15
16 #include "llvm/Support/Chrono.h"
17 #include "llvm/Support/ManagedStatic.h"
18 #include "llvm/Support/Path.h"
19 #include "llvm/Support/Signals.h"
20 #include "llvm/Support/Threading.h"
21 #include "llvm/Support/raw_ostream.h"
22
23 #include <chrono>
24 #include <cstdarg>
25 #include <mutex>
26 #include <utility>
27
28 #include <cassert>
29 #if defined(_WIN32)
30 #include <process.h>
31 #else
32 #include <unistd.h>
33 #include <pthread.h>
34 #endif
35
36 using namespace lldb_private;
37
38 llvm::ManagedStatic<Log::ChannelMap> Log::g_channel_map;
39
ForEachCategory(const Log::ChannelMap::value_type & entry,llvm::function_ref<void (llvm::StringRef,llvm::StringRef)> lambda)40 void Log::ForEachCategory(
41 const Log::ChannelMap::value_type &entry,
42 llvm::function_ref<void(llvm::StringRef, llvm::StringRef)> lambda) {
43 lambda("all", "all available logging categories");
44 lambda("default", "default set of logging categories");
45 for (const auto &category : entry.second.m_channel.categories)
46 lambda(category.name, category.description);
47 }
48
ListCategories(llvm::raw_ostream & stream,const ChannelMap::value_type & entry)49 void Log::ListCategories(llvm::raw_ostream &stream,
50 const ChannelMap::value_type &entry) {
51 stream << llvm::formatv("Logging categories for '{0}':\n", entry.first());
52 ForEachCategory(entry,
53 [&stream](llvm::StringRef name, llvm::StringRef description) {
54 stream << llvm::formatv(" {0} - {1}\n", name, description);
55 });
56 }
57
GetFlags(llvm::raw_ostream & stream,const ChannelMap::value_type & entry,llvm::ArrayRef<const char * > categories)58 uint32_t Log::GetFlags(llvm::raw_ostream &stream, const ChannelMap::value_type &entry,
59 llvm::ArrayRef<const char *> categories) {
60 bool list_categories = false;
61 uint32_t flags = 0;
62 for (const char *category : categories) {
63 if (llvm::StringRef("all").equals_insensitive(category)) {
64 flags |= UINT32_MAX;
65 continue;
66 }
67 if (llvm::StringRef("default").equals_insensitive(category)) {
68 flags |= entry.second.m_channel.default_flags;
69 continue;
70 }
71 auto cat = llvm::find_if(entry.second.m_channel.categories,
72 [&](const Log::Category &c) {
73 return c.name.equals_insensitive(category);
74 });
75 if (cat != entry.second.m_channel.categories.end()) {
76 flags |= cat->flag;
77 continue;
78 }
79 stream << llvm::formatv("error: unrecognized log category '{0}'\n",
80 category);
81 list_categories = true;
82 }
83 if (list_categories)
84 ListCategories(stream, entry);
85 return flags;
86 }
87
Enable(const std::shared_ptr<llvm::raw_ostream> & stream_sp,uint32_t options,uint32_t flags)88 void Log::Enable(const std::shared_ptr<llvm::raw_ostream> &stream_sp,
89 uint32_t options, uint32_t flags) {
90 llvm::sys::ScopedWriter lock(m_mutex);
91
92 uint32_t mask = m_mask.fetch_or(flags, std::memory_order_relaxed);
93 if (mask | flags) {
94 m_options.store(options, std::memory_order_relaxed);
95 m_stream_sp = stream_sp;
96 m_channel.log_ptr.store(this, std::memory_order_relaxed);
97 }
98 }
99
Disable(uint32_t flags)100 void Log::Disable(uint32_t flags) {
101 llvm::sys::ScopedWriter lock(m_mutex);
102
103 uint32_t mask = m_mask.fetch_and(~flags, std::memory_order_relaxed);
104 if (!(mask & ~flags)) {
105 m_stream_sp.reset();
106 m_channel.log_ptr.store(nullptr, std::memory_order_relaxed);
107 }
108 }
109
GetOptions() const110 const Flags Log::GetOptions() const {
111 return m_options.load(std::memory_order_relaxed);
112 }
113
GetMask() const114 const Flags Log::GetMask() const {
115 return m_mask.load(std::memory_order_relaxed);
116 }
117
PutCString(const char * cstr)118 void Log::PutCString(const char *cstr) { Printf("%s", cstr); }
PutString(llvm::StringRef str)119 void Log::PutString(llvm::StringRef str) { PutCString(str.str().c_str()); }
120
121 // Simple variable argument logging with flags.
Printf(const char * format,...)122 void Log::Printf(const char *format, ...) {
123 va_list args;
124 va_start(args, format);
125 VAPrintf(format, args);
126 va_end(args);
127 }
128
129 // All logging eventually boils down to this function call. If we have a
130 // callback registered, then we call the logging callback. If we have a valid
131 // file handle, we also log to the file.
VAPrintf(const char * format,va_list args)132 void Log::VAPrintf(const char *format, va_list args) {
133 llvm::SmallString<64> FinalMessage;
134 llvm::raw_svector_ostream Stream(FinalMessage);
135 WriteHeader(Stream, "", "");
136
137 llvm::SmallString<64> Content;
138 lldb_private::VASprintf(Content, format, args);
139
140 Stream << Content << "\n";
141
142 WriteMessage(std::string(FinalMessage.str()));
143 }
144
145 // Printing of errors that are not fatal.
Error(const char * format,...)146 void Log::Error(const char *format, ...) {
147 va_list args;
148 va_start(args, format);
149 VAError(format, args);
150 va_end(args);
151 }
152
VAError(const char * format,va_list args)153 void Log::VAError(const char *format, va_list args) {
154 llvm::SmallString<64> Content;
155 VASprintf(Content, format, args);
156
157 Printf("error: %s", Content.c_str());
158 }
159
160 // Printing of warnings that are not fatal only if verbose mode is enabled.
Verbose(const char * format,...)161 void Log::Verbose(const char *format, ...) {
162 if (!GetVerbose())
163 return;
164
165 va_list args;
166 va_start(args, format);
167 VAPrintf(format, args);
168 va_end(args);
169 }
170
171 // Printing of warnings that are not fatal.
Warning(const char * format,...)172 void Log::Warning(const char *format, ...) {
173 llvm::SmallString<64> Content;
174 va_list args;
175 va_start(args, format);
176 VASprintf(Content, format, args);
177 va_end(args);
178
179 Printf("warning: %s", Content.c_str());
180 }
181
Initialize()182 void Log::Initialize() {
183 #ifdef LLVM_ON_UNIX
184 pthread_atfork(nullptr, nullptr, &Log::DisableLoggingChild);
185 #endif
186 InitializeLldbChannel();
187 }
188
Register(llvm::StringRef name,Channel & channel)189 void Log::Register(llvm::StringRef name, Channel &channel) {
190 auto iter = g_channel_map->try_emplace(name, channel);
191 assert(iter.second == true);
192 (void)iter;
193 }
194
Unregister(llvm::StringRef name)195 void Log::Unregister(llvm::StringRef name) {
196 auto iter = g_channel_map->find(name);
197 assert(iter != g_channel_map->end());
198 iter->second.Disable(UINT32_MAX);
199 g_channel_map->erase(iter);
200 }
201
EnableLogChannel(const std::shared_ptr<llvm::raw_ostream> & log_stream_sp,uint32_t log_options,llvm::StringRef channel,llvm::ArrayRef<const char * > categories,llvm::raw_ostream & error_stream)202 bool Log::EnableLogChannel(
203 const std::shared_ptr<llvm::raw_ostream> &log_stream_sp,
204 uint32_t log_options, llvm::StringRef channel,
205 llvm::ArrayRef<const char *> categories, llvm::raw_ostream &error_stream) {
206 auto iter = g_channel_map->find(channel);
207 if (iter == g_channel_map->end()) {
208 error_stream << llvm::formatv("Invalid log channel '{0}'.\n", channel);
209 return false;
210 }
211 uint32_t flags = categories.empty()
212 ? iter->second.m_channel.default_flags
213 : GetFlags(error_stream, *iter, categories);
214 iter->second.Enable(log_stream_sp, log_options, flags);
215 return true;
216 }
217
DisableLogChannel(llvm::StringRef channel,llvm::ArrayRef<const char * > categories,llvm::raw_ostream & error_stream)218 bool Log::DisableLogChannel(llvm::StringRef channel,
219 llvm::ArrayRef<const char *> categories,
220 llvm::raw_ostream &error_stream) {
221 auto iter = g_channel_map->find(channel);
222 if (iter == g_channel_map->end()) {
223 error_stream << llvm::formatv("Invalid log channel '{0}'.\n", channel);
224 return false;
225 }
226 uint32_t flags = categories.empty()
227 ? UINT32_MAX
228 : GetFlags(error_stream, *iter, categories);
229 iter->second.Disable(flags);
230 return true;
231 }
232
ListChannelCategories(llvm::StringRef channel,llvm::raw_ostream & stream)233 bool Log::ListChannelCategories(llvm::StringRef channel,
234 llvm::raw_ostream &stream) {
235 auto ch = g_channel_map->find(channel);
236 if (ch == g_channel_map->end()) {
237 stream << llvm::formatv("Invalid log channel '{0}'.\n", channel);
238 return false;
239 }
240 ListCategories(stream, *ch);
241 return true;
242 }
243
DisableAllLogChannels()244 void Log::DisableAllLogChannels() {
245 for (auto &entry : *g_channel_map)
246 entry.second.Disable(UINT32_MAX);
247 }
248
ForEachChannelCategory(llvm::StringRef channel,llvm::function_ref<void (llvm::StringRef,llvm::StringRef)> lambda)249 void Log::ForEachChannelCategory(
250 llvm::StringRef channel,
251 llvm::function_ref<void(llvm::StringRef, llvm::StringRef)> lambda) {
252 auto ch = g_channel_map->find(channel);
253 if (ch == g_channel_map->end())
254 return;
255
256 ForEachCategory(*ch, lambda);
257 }
258
ListChannels()259 std::vector<llvm::StringRef> Log::ListChannels() {
260 std::vector<llvm::StringRef> result;
261 for (const auto &channel : *g_channel_map)
262 result.push_back(channel.first());
263 return result;
264 }
265
ListAllLogChannels(llvm::raw_ostream & stream)266 void Log::ListAllLogChannels(llvm::raw_ostream &stream) {
267 if (g_channel_map->empty()) {
268 stream << "No logging channels are currently registered.\n";
269 return;
270 }
271
272 for (const auto &channel : *g_channel_map)
273 ListCategories(stream, channel);
274 }
275
GetVerbose() const276 bool Log::GetVerbose() const {
277 return m_options.load(std::memory_order_relaxed) & LLDB_LOG_OPTION_VERBOSE;
278 }
279
WriteHeader(llvm::raw_ostream & OS,llvm::StringRef file,llvm::StringRef function)280 void Log::WriteHeader(llvm::raw_ostream &OS, llvm::StringRef file,
281 llvm::StringRef function) {
282 Flags options = GetOptions();
283 static uint32_t g_sequence_id = 0;
284 // Add a sequence ID if requested
285 if (options.Test(LLDB_LOG_OPTION_PREPEND_SEQUENCE))
286 OS << ++g_sequence_id << " ";
287
288 // Timestamp if requested
289 if (options.Test(LLDB_LOG_OPTION_PREPEND_TIMESTAMP)) {
290 auto now = std::chrono::duration<double>(
291 std::chrono::system_clock::now().time_since_epoch());
292 OS << llvm::formatv("{0:f9} ", now.count());
293 }
294
295 // Add the process and thread if requested
296 if (options.Test(LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD))
297 OS << llvm::formatv("[{0,0+4}/{1,0+4}] ", getpid(),
298 llvm::get_threadid());
299
300 // Add the thread name if requested
301 if (options.Test(LLDB_LOG_OPTION_PREPEND_THREAD_NAME)) {
302 llvm::SmallString<32> thread_name;
303 llvm::get_thread_name(thread_name);
304
305 llvm::SmallString<12> format_str;
306 llvm::raw_svector_ostream format_os(format_str);
307 format_os << "{0,-" << llvm::alignTo<16>(thread_name.size()) << "} ";
308 OS << llvm::formatv(format_str.c_str(), thread_name);
309 }
310
311 if (options.Test(LLDB_LOG_OPTION_BACKTRACE))
312 llvm::sys::PrintStackTrace(OS);
313
314 if (options.Test(LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION) &&
315 (!file.empty() || !function.empty())) {
316 file = llvm::sys::path::filename(file).take_front(40);
317 function = function.take_front(40);
318 OS << llvm::formatv("{0,-60:60} ", (file + ":" + function).str());
319 }
320 }
321
WriteMessage(const std::string & message)322 void Log::WriteMessage(const std::string &message) {
323 // Make a copy of our stream shared pointer in case someone disables our log
324 // while we are logging and releases the stream
325 auto stream_sp = GetStream();
326 if (!stream_sp)
327 return;
328
329 Flags options = GetOptions();
330 if (options.Test(LLDB_LOG_OPTION_THREADSAFE)) {
331 static std::recursive_mutex g_LogThreadedMutex;
332 std::lock_guard<std::recursive_mutex> guard(g_LogThreadedMutex);
333 *stream_sp << message;
334 stream_sp->flush();
335 } else {
336 *stream_sp << message;
337 stream_sp->flush();
338 }
339 }
340
Format(llvm::StringRef file,llvm::StringRef function,const llvm::formatv_object_base & payload)341 void Log::Format(llvm::StringRef file, llvm::StringRef function,
342 const llvm::formatv_object_base &payload) {
343 std::string message_string;
344 llvm::raw_string_ostream message(message_string);
345 WriteHeader(message, file, function);
346 message << payload << "\n";
347 WriteMessage(message.str());
348 }
349
DisableLoggingChild()350 void Log::DisableLoggingChild() {
351 // Disable logging by clearing out the atomic variable after forking -- if we
352 // forked while another thread held the channel mutex, we would deadlock when
353 // trying to write to the log.
354 for (auto &c: *g_channel_map)
355 c.second.m_channel.log_ptr.store(nullptr, std::memory_order_relaxed);
356 }
357