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