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