xref: /openbsd-src/gnu/llvm/lldb/source/Utility/Log.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1dda28197Spatrick //===-- Log.cpp -----------------------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "lldb/Utility/Log.h"
10061da546Spatrick #include "lldb/Utility/VASPrintf.h"
11061da546Spatrick 
12061da546Spatrick #include "llvm/ADT/SmallString.h"
13061da546Spatrick #include "llvm/ADT/Twine.h"
14061da546Spatrick #include "llvm/ADT/iterator.h"
15061da546Spatrick 
16*f6aab3d8Srobert #include "llvm/Support/Casting.h"
17061da546Spatrick #include "llvm/Support/Chrono.h"
18061da546Spatrick #include "llvm/Support/ManagedStatic.h"
19061da546Spatrick #include "llvm/Support/Path.h"
20061da546Spatrick #include "llvm/Support/Signals.h"
21061da546Spatrick #include "llvm/Support/Threading.h"
22061da546Spatrick #include "llvm/Support/raw_ostream.h"
23061da546Spatrick 
24061da546Spatrick #include <chrono>
25061da546Spatrick #include <cstdarg>
26061da546Spatrick #include <mutex>
27061da546Spatrick #include <utility>
28061da546Spatrick 
29be691f3bSpatrick #include <cassert>
30061da546Spatrick #if defined(_WIN32)
31061da546Spatrick #include <process.h>
32061da546Spatrick #else
33061da546Spatrick #include <unistd.h>
34061da546Spatrick #endif
35061da546Spatrick 
36061da546Spatrick using namespace lldb_private;
37061da546Spatrick 
38*f6aab3d8Srobert char LogHandler::ID;
39*f6aab3d8Srobert char StreamLogHandler::ID;
40*f6aab3d8Srobert char CallbackLogHandler::ID;
41*f6aab3d8Srobert char RotatingLogHandler::ID;
42*f6aab3d8Srobert 
43061da546Spatrick llvm::ManagedStatic<Log::ChannelMap> Log::g_channel_map;
44061da546Spatrick 
ForEachCategory(const Log::ChannelMap::value_type & entry,llvm::function_ref<void (llvm::StringRef,llvm::StringRef)> lambda)45061da546Spatrick void Log::ForEachCategory(
46061da546Spatrick     const Log::ChannelMap::value_type &entry,
47061da546Spatrick     llvm::function_ref<void(llvm::StringRef, llvm::StringRef)> lambda) {
48061da546Spatrick   lambda("all", "all available logging categories");
49061da546Spatrick   lambda("default", "default set of logging categories");
50061da546Spatrick   for (const auto &category : entry.second.m_channel.categories)
51061da546Spatrick     lambda(category.name, category.description);
52061da546Spatrick }
53061da546Spatrick 
ListCategories(llvm::raw_ostream & stream,const ChannelMap::value_type & entry)54061da546Spatrick void Log::ListCategories(llvm::raw_ostream &stream,
55061da546Spatrick                          const ChannelMap::value_type &entry) {
56061da546Spatrick   stream << llvm::formatv("Logging categories for '{0}':\n", entry.first());
57061da546Spatrick   ForEachCategory(entry,
58061da546Spatrick                   [&stream](llvm::StringRef name, llvm::StringRef description) {
59061da546Spatrick                     stream << llvm::formatv("  {0} - {1}\n", name, description);
60061da546Spatrick                   });
61061da546Spatrick }
62061da546Spatrick 
GetFlags(llvm::raw_ostream & stream,const ChannelMap::value_type & entry,llvm::ArrayRef<const char * > categories)63*f6aab3d8Srobert Log::MaskType Log::GetFlags(llvm::raw_ostream &stream,
64*f6aab3d8Srobert                             const ChannelMap::value_type &entry,
65061da546Spatrick                             llvm::ArrayRef<const char *> categories) {
66061da546Spatrick   bool list_categories = false;
67*f6aab3d8Srobert   Log::MaskType flags = 0;
68061da546Spatrick   for (const char *category : categories) {
69be691f3bSpatrick     if (llvm::StringRef("all").equals_insensitive(category)) {
70*f6aab3d8Srobert       flags |= std::numeric_limits<Log::MaskType>::max();
71061da546Spatrick       continue;
72061da546Spatrick     }
73be691f3bSpatrick     if (llvm::StringRef("default").equals_insensitive(category)) {
74061da546Spatrick       flags |= entry.second.m_channel.default_flags;
75061da546Spatrick       continue;
76061da546Spatrick     }
77be691f3bSpatrick     auto cat = llvm::find_if(entry.second.m_channel.categories,
78be691f3bSpatrick                              [&](const Log::Category &c) {
79be691f3bSpatrick                                return c.name.equals_insensitive(category);
80be691f3bSpatrick                              });
81061da546Spatrick     if (cat != entry.second.m_channel.categories.end()) {
82061da546Spatrick       flags |= cat->flag;
83061da546Spatrick       continue;
84061da546Spatrick     }
85061da546Spatrick     stream << llvm::formatv("error: unrecognized log category '{0}'\n",
86061da546Spatrick                             category);
87061da546Spatrick     list_categories = true;
88061da546Spatrick   }
89061da546Spatrick   if (list_categories)
90061da546Spatrick     ListCategories(stream, entry);
91061da546Spatrick   return flags;
92061da546Spatrick }
93061da546Spatrick 
Enable(const std::shared_ptr<LogHandler> & handler_sp,uint32_t options,Log::MaskType flags)94*f6aab3d8Srobert void Log::Enable(const std::shared_ptr<LogHandler> &handler_sp,
95*f6aab3d8Srobert                  uint32_t options, Log::MaskType flags) {
96061da546Spatrick   llvm::sys::ScopedWriter lock(m_mutex);
97061da546Spatrick 
98*f6aab3d8Srobert   MaskType mask = m_mask.fetch_or(flags, std::memory_order_relaxed);
99061da546Spatrick   if (mask | flags) {
100061da546Spatrick     m_options.store(options, std::memory_order_relaxed);
101*f6aab3d8Srobert     m_handler = handler_sp;
102061da546Spatrick     m_channel.log_ptr.store(this, std::memory_order_relaxed);
103061da546Spatrick   }
104061da546Spatrick }
105061da546Spatrick 
Disable(Log::MaskType flags)106*f6aab3d8Srobert void Log::Disable(Log::MaskType flags) {
107061da546Spatrick   llvm::sys::ScopedWriter lock(m_mutex);
108061da546Spatrick 
109*f6aab3d8Srobert   MaskType mask = m_mask.fetch_and(~flags, std::memory_order_relaxed);
110061da546Spatrick   if (!(mask & ~flags)) {
111*f6aab3d8Srobert     m_handler.reset();
112061da546Spatrick     m_channel.log_ptr.store(nullptr, std::memory_order_relaxed);
113061da546Spatrick   }
114061da546Spatrick }
115061da546Spatrick 
Dump(llvm::raw_ostream & output_stream)116*f6aab3d8Srobert bool Log::Dump(llvm::raw_ostream &output_stream) {
117*f6aab3d8Srobert   llvm::sys::ScopedReader lock(m_mutex);
118*f6aab3d8Srobert   if (RotatingLogHandler *handler =
119*f6aab3d8Srobert           llvm::dyn_cast_or_null<RotatingLogHandler>(m_handler.get())) {
120*f6aab3d8Srobert     handler->Dump(output_stream);
121*f6aab3d8Srobert     return true;
122*f6aab3d8Srobert   }
123*f6aab3d8Srobert   return false;
124*f6aab3d8Srobert }
125*f6aab3d8Srobert 
GetOptions() const126061da546Spatrick const Flags Log::GetOptions() const {
127061da546Spatrick   return m_options.load(std::memory_order_relaxed);
128061da546Spatrick }
129061da546Spatrick 
GetMask() const130*f6aab3d8Srobert Log::MaskType Log::GetMask() const {
131061da546Spatrick   return m_mask.load(std::memory_order_relaxed);
132061da546Spatrick }
133061da546Spatrick 
PutCString(const char * cstr)134061da546Spatrick void Log::PutCString(const char *cstr) { Printf("%s", cstr); }
PutString(llvm::StringRef str)135061da546Spatrick void Log::PutString(llvm::StringRef str) { PutCString(str.str().c_str()); }
136061da546Spatrick 
137061da546Spatrick // Simple variable argument logging with flags.
Printf(const char * format,...)138061da546Spatrick void Log::Printf(const char *format, ...) {
139061da546Spatrick   va_list args;
140061da546Spatrick   va_start(args, format);
141061da546Spatrick   VAPrintf(format, args);
142061da546Spatrick   va_end(args);
143061da546Spatrick }
144061da546Spatrick 
145061da546Spatrick // All logging eventually boils down to this function call. If we have a
146061da546Spatrick // callback registered, then we call the logging callback. If we have a valid
147061da546Spatrick // file handle, we also log to the file.
VAPrintf(const char * format,va_list args)148061da546Spatrick void Log::VAPrintf(const char *format, va_list args) {
149061da546Spatrick   llvm::SmallString<64> FinalMessage;
150061da546Spatrick   llvm::raw_svector_ostream Stream(FinalMessage);
151061da546Spatrick   WriteHeader(Stream, "", "");
152061da546Spatrick 
153061da546Spatrick   llvm::SmallString<64> Content;
154061da546Spatrick   lldb_private::VASprintf(Content, format, args);
155061da546Spatrick 
156061da546Spatrick   Stream << Content << "\n";
157061da546Spatrick 
158dda28197Spatrick   WriteMessage(std::string(FinalMessage.str()));
159061da546Spatrick }
160061da546Spatrick 
161061da546Spatrick // Printing of errors that are not fatal.
Error(const char * format,...)162061da546Spatrick void Log::Error(const char *format, ...) {
163061da546Spatrick   va_list args;
164061da546Spatrick   va_start(args, format);
165061da546Spatrick   VAError(format, args);
166061da546Spatrick   va_end(args);
167061da546Spatrick }
168061da546Spatrick 
VAError(const char * format,va_list args)169061da546Spatrick void Log::VAError(const char *format, va_list args) {
170061da546Spatrick   llvm::SmallString<64> Content;
171061da546Spatrick   VASprintf(Content, format, args);
172061da546Spatrick 
173061da546Spatrick   Printf("error: %s", Content.c_str());
174061da546Spatrick }
175061da546Spatrick 
176061da546Spatrick // Printing of warnings that are not fatal only if verbose mode is enabled.
Verbose(const char * format,...)177061da546Spatrick void Log::Verbose(const char *format, ...) {
178061da546Spatrick   if (!GetVerbose())
179061da546Spatrick     return;
180061da546Spatrick 
181061da546Spatrick   va_list args;
182061da546Spatrick   va_start(args, format);
183061da546Spatrick   VAPrintf(format, args);
184061da546Spatrick   va_end(args);
185061da546Spatrick }
186061da546Spatrick 
187061da546Spatrick // Printing of warnings that are not fatal.
Warning(const char * format,...)188061da546Spatrick void Log::Warning(const char *format, ...) {
189061da546Spatrick   llvm::SmallString<64> Content;
190061da546Spatrick   va_list args;
191061da546Spatrick   va_start(args, format);
192061da546Spatrick   VASprintf(Content, format, args);
193061da546Spatrick   va_end(args);
194061da546Spatrick 
195061da546Spatrick   Printf("warning: %s", Content.c_str());
196061da546Spatrick }
197061da546Spatrick 
Register(llvm::StringRef name,Channel & channel)198061da546Spatrick void Log::Register(llvm::StringRef name, Channel &channel) {
199061da546Spatrick   auto iter = g_channel_map->try_emplace(name, channel);
200061da546Spatrick   assert(iter.second == true);
201061da546Spatrick   (void)iter;
202061da546Spatrick }
203061da546Spatrick 
Unregister(llvm::StringRef name)204061da546Spatrick void Log::Unregister(llvm::StringRef name) {
205061da546Spatrick   auto iter = g_channel_map->find(name);
206061da546Spatrick   assert(iter != g_channel_map->end());
207*f6aab3d8Srobert   iter->second.Disable(std::numeric_limits<MaskType>::max());
208061da546Spatrick   g_channel_map->erase(iter);
209061da546Spatrick }
210061da546Spatrick 
EnableLogChannel(const std::shared_ptr<LogHandler> & log_handler_sp,uint32_t log_options,llvm::StringRef channel,llvm::ArrayRef<const char * > categories,llvm::raw_ostream & error_stream)211*f6aab3d8Srobert bool Log::EnableLogChannel(const std::shared_ptr<LogHandler> &log_handler_sp,
212061da546Spatrick                            uint32_t log_options, llvm::StringRef channel,
213*f6aab3d8Srobert                            llvm::ArrayRef<const char *> categories,
214*f6aab3d8Srobert                            llvm::raw_ostream &error_stream) {
215061da546Spatrick   auto iter = g_channel_map->find(channel);
216061da546Spatrick   if (iter == g_channel_map->end()) {
217061da546Spatrick     error_stream << llvm::formatv("Invalid log channel '{0}'.\n", channel);
218061da546Spatrick     return false;
219061da546Spatrick   }
220*f6aab3d8Srobert   MaskType flags = categories.empty()
221061da546Spatrick                        ? iter->second.m_channel.default_flags
222061da546Spatrick                        : GetFlags(error_stream, *iter, categories);
223*f6aab3d8Srobert   iter->second.Enable(log_handler_sp, log_options, flags);
224061da546Spatrick   return true;
225061da546Spatrick }
226061da546Spatrick 
DisableLogChannel(llvm::StringRef channel,llvm::ArrayRef<const char * > categories,llvm::raw_ostream & error_stream)227061da546Spatrick bool Log::DisableLogChannel(llvm::StringRef channel,
228061da546Spatrick                             llvm::ArrayRef<const char *> categories,
229061da546Spatrick                             llvm::raw_ostream &error_stream) {
230061da546Spatrick   auto iter = g_channel_map->find(channel);
231061da546Spatrick   if (iter == g_channel_map->end()) {
232061da546Spatrick     error_stream << llvm::formatv("Invalid log channel '{0}'.\n", channel);
233061da546Spatrick     return false;
234061da546Spatrick   }
235*f6aab3d8Srobert   MaskType flags = categories.empty()
236*f6aab3d8Srobert                        ? std::numeric_limits<MaskType>::max()
237061da546Spatrick                        : GetFlags(error_stream, *iter, categories);
238061da546Spatrick   iter->second.Disable(flags);
239061da546Spatrick   return true;
240061da546Spatrick }
241061da546Spatrick 
DumpLogChannel(llvm::StringRef channel,llvm::raw_ostream & output_stream,llvm::raw_ostream & error_stream)242*f6aab3d8Srobert bool Log::DumpLogChannel(llvm::StringRef channel,
243*f6aab3d8Srobert                          llvm::raw_ostream &output_stream,
244*f6aab3d8Srobert                          llvm::raw_ostream &error_stream) {
245*f6aab3d8Srobert   auto iter = g_channel_map->find(channel);
246*f6aab3d8Srobert   if (iter == g_channel_map->end()) {
247*f6aab3d8Srobert     error_stream << llvm::formatv("Invalid log channel '{0}'.\n", channel);
248*f6aab3d8Srobert     return false;
249*f6aab3d8Srobert   }
250*f6aab3d8Srobert   if (!iter->second.Dump(output_stream)) {
251*f6aab3d8Srobert     error_stream << llvm::formatv(
252*f6aab3d8Srobert         "log channel '{0}' does not support dumping.\n", channel);
253*f6aab3d8Srobert     return false;
254*f6aab3d8Srobert   }
255*f6aab3d8Srobert   return true;
256*f6aab3d8Srobert }
257*f6aab3d8Srobert 
ListChannelCategories(llvm::StringRef channel,llvm::raw_ostream & stream)258061da546Spatrick bool Log::ListChannelCategories(llvm::StringRef channel,
259061da546Spatrick                                 llvm::raw_ostream &stream) {
260061da546Spatrick   auto ch = g_channel_map->find(channel);
261061da546Spatrick   if (ch == g_channel_map->end()) {
262061da546Spatrick     stream << llvm::formatv("Invalid log channel '{0}'.\n", channel);
263061da546Spatrick     return false;
264061da546Spatrick   }
265061da546Spatrick   ListCategories(stream, *ch);
266061da546Spatrick   return true;
267061da546Spatrick }
268061da546Spatrick 
DisableAllLogChannels()269061da546Spatrick void Log::DisableAllLogChannels() {
270061da546Spatrick   for (auto &entry : *g_channel_map)
271*f6aab3d8Srobert     entry.second.Disable(std::numeric_limits<MaskType>::max());
272061da546Spatrick }
273061da546Spatrick 
ForEachChannelCategory(llvm::StringRef channel,llvm::function_ref<void (llvm::StringRef,llvm::StringRef)> lambda)274061da546Spatrick void Log::ForEachChannelCategory(
275061da546Spatrick     llvm::StringRef channel,
276061da546Spatrick     llvm::function_ref<void(llvm::StringRef, llvm::StringRef)> lambda) {
277061da546Spatrick   auto ch = g_channel_map->find(channel);
278061da546Spatrick   if (ch == g_channel_map->end())
279061da546Spatrick     return;
280061da546Spatrick 
281061da546Spatrick   ForEachCategory(*ch, lambda);
282061da546Spatrick }
283061da546Spatrick 
ListChannels()284061da546Spatrick std::vector<llvm::StringRef> Log::ListChannels() {
285061da546Spatrick   std::vector<llvm::StringRef> result;
286061da546Spatrick   for (const auto &channel : *g_channel_map)
287061da546Spatrick     result.push_back(channel.first());
288061da546Spatrick   return result;
289061da546Spatrick }
290061da546Spatrick 
ListAllLogChannels(llvm::raw_ostream & stream)291061da546Spatrick void Log::ListAllLogChannels(llvm::raw_ostream &stream) {
292061da546Spatrick   if (g_channel_map->empty()) {
293061da546Spatrick     stream << "No logging channels are currently registered.\n";
294061da546Spatrick     return;
295061da546Spatrick   }
296061da546Spatrick 
297061da546Spatrick   for (const auto &channel : *g_channel_map)
298061da546Spatrick     ListCategories(stream, channel);
299061da546Spatrick }
300061da546Spatrick 
GetVerbose() const301061da546Spatrick bool Log::GetVerbose() const {
302061da546Spatrick   return m_options.load(std::memory_order_relaxed) & LLDB_LOG_OPTION_VERBOSE;
303061da546Spatrick }
304061da546Spatrick 
WriteHeader(llvm::raw_ostream & OS,llvm::StringRef file,llvm::StringRef function)305061da546Spatrick void Log::WriteHeader(llvm::raw_ostream &OS, llvm::StringRef file,
306061da546Spatrick                       llvm::StringRef function) {
307061da546Spatrick   Flags options = GetOptions();
308061da546Spatrick   static uint32_t g_sequence_id = 0;
309061da546Spatrick   // Add a sequence ID if requested
310061da546Spatrick   if (options.Test(LLDB_LOG_OPTION_PREPEND_SEQUENCE))
311061da546Spatrick     OS << ++g_sequence_id << " ";
312061da546Spatrick 
313061da546Spatrick   // Timestamp if requested
314061da546Spatrick   if (options.Test(LLDB_LOG_OPTION_PREPEND_TIMESTAMP)) {
315061da546Spatrick     auto now = std::chrono::duration<double>(
316061da546Spatrick         std::chrono::system_clock::now().time_since_epoch());
317061da546Spatrick     OS << llvm::formatv("{0:f9} ", now.count());
318061da546Spatrick   }
319061da546Spatrick 
320061da546Spatrick   // Add the process and thread if requested
321061da546Spatrick   if (options.Test(LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD))
322061da546Spatrick     OS << llvm::formatv("[{0,0+4}/{1,0+4}] ", getpid(),
323061da546Spatrick                         llvm::get_threadid());
324061da546Spatrick 
325061da546Spatrick   // Add the thread name if requested
326061da546Spatrick   if (options.Test(LLDB_LOG_OPTION_PREPEND_THREAD_NAME)) {
327061da546Spatrick     llvm::SmallString<32> thread_name;
328061da546Spatrick     llvm::get_thread_name(thread_name);
329061da546Spatrick 
330061da546Spatrick     llvm::SmallString<12> format_str;
331061da546Spatrick     llvm::raw_svector_ostream format_os(format_str);
332061da546Spatrick     format_os << "{0,-" << llvm::alignTo<16>(thread_name.size()) << "} ";
333061da546Spatrick     OS << llvm::formatv(format_str.c_str(), thread_name);
334061da546Spatrick   }
335061da546Spatrick 
336061da546Spatrick   if (options.Test(LLDB_LOG_OPTION_BACKTRACE))
337061da546Spatrick     llvm::sys::PrintStackTrace(OS);
338061da546Spatrick 
339061da546Spatrick   if (options.Test(LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION) &&
340061da546Spatrick       (!file.empty() || !function.empty())) {
341061da546Spatrick     file = llvm::sys::path::filename(file).take_front(40);
342061da546Spatrick     function = function.take_front(40);
343061da546Spatrick     OS << llvm::formatv("{0,-60:60} ", (file + ":" + function).str());
344061da546Spatrick   }
345061da546Spatrick }
346061da546Spatrick 
WriteMessage(const std::string & message)347061da546Spatrick void Log::WriteMessage(const std::string &message) {
348061da546Spatrick   // Make a copy of our stream shared pointer in case someone disables our log
349061da546Spatrick   // while we are logging and releases the stream
350*f6aab3d8Srobert   auto handler_sp = GetHandler();
351*f6aab3d8Srobert   if (!handler_sp)
352061da546Spatrick     return;
353*f6aab3d8Srobert   handler_sp->Emit(message);
354061da546Spatrick }
355061da546Spatrick 
Format(llvm::StringRef file,llvm::StringRef function,const llvm::formatv_object_base & payload)356061da546Spatrick void Log::Format(llvm::StringRef file, llvm::StringRef function,
357061da546Spatrick                  const llvm::formatv_object_base &payload) {
358061da546Spatrick   std::string message_string;
359061da546Spatrick   llvm::raw_string_ostream message(message_string);
360061da546Spatrick   WriteHeader(message, file, function);
361061da546Spatrick   message << payload << "\n";
362061da546Spatrick   WriteMessage(message.str());
363061da546Spatrick }
364061da546Spatrick 
StreamLogHandler(int fd,bool should_close,size_t buffer_size)365*f6aab3d8Srobert StreamLogHandler::StreamLogHandler(int fd, bool should_close,
366*f6aab3d8Srobert                                    size_t buffer_size)
367*f6aab3d8Srobert     : m_stream(fd, should_close, buffer_size == 0) {
368*f6aab3d8Srobert   if (buffer_size > 0)
369*f6aab3d8Srobert     m_stream.SetBufferSize(buffer_size);
370*f6aab3d8Srobert }
371*f6aab3d8Srobert 
~StreamLogHandler()372*f6aab3d8Srobert StreamLogHandler::~StreamLogHandler() { Flush(); }
373*f6aab3d8Srobert 
Flush()374*f6aab3d8Srobert void StreamLogHandler::Flush() {
375*f6aab3d8Srobert   std::lock_guard<std::mutex> guard(m_mutex);
376*f6aab3d8Srobert   m_stream.flush();
377*f6aab3d8Srobert }
378*f6aab3d8Srobert 
Emit(llvm::StringRef message)379*f6aab3d8Srobert void StreamLogHandler::Emit(llvm::StringRef message) {
380*f6aab3d8Srobert   if (m_stream.GetBufferSize() > 0) {
381*f6aab3d8Srobert     std::lock_guard<std::mutex> guard(m_mutex);
382*f6aab3d8Srobert     m_stream << message;
383*f6aab3d8Srobert   } else {
384*f6aab3d8Srobert     m_stream << message;
385*f6aab3d8Srobert   }
386*f6aab3d8Srobert }
387*f6aab3d8Srobert 
CallbackLogHandler(lldb::LogOutputCallback callback,void * baton)388*f6aab3d8Srobert CallbackLogHandler::CallbackLogHandler(lldb::LogOutputCallback callback,
389*f6aab3d8Srobert                                        void *baton)
390*f6aab3d8Srobert     : m_callback(callback), m_baton(baton) {}
391*f6aab3d8Srobert 
Emit(llvm::StringRef message)392*f6aab3d8Srobert void CallbackLogHandler::Emit(llvm::StringRef message) {
393*f6aab3d8Srobert   m_callback(message.data(), m_baton);
394*f6aab3d8Srobert }
395*f6aab3d8Srobert 
RotatingLogHandler(size_t size)396*f6aab3d8Srobert RotatingLogHandler::RotatingLogHandler(size_t size)
397*f6aab3d8Srobert     : m_messages(std::make_unique<std::string[]>(size)), m_size(size) {}
398*f6aab3d8Srobert 
Emit(llvm::StringRef message)399*f6aab3d8Srobert void RotatingLogHandler::Emit(llvm::StringRef message) {
400*f6aab3d8Srobert   std::lock_guard<std::mutex> guard(m_mutex);
401*f6aab3d8Srobert   ++m_total_count;
402*f6aab3d8Srobert   const size_t index = m_next_index;
403*f6aab3d8Srobert   m_next_index = NormalizeIndex(index + 1);
404*f6aab3d8Srobert   m_messages[index] = message.str();
405*f6aab3d8Srobert }
406*f6aab3d8Srobert 
NormalizeIndex(size_t i) const407*f6aab3d8Srobert size_t RotatingLogHandler::NormalizeIndex(size_t i) const { return i % m_size; }
408*f6aab3d8Srobert 
GetNumMessages() const409*f6aab3d8Srobert size_t RotatingLogHandler::GetNumMessages() const {
410*f6aab3d8Srobert   return m_total_count < m_size ? m_total_count : m_size;
411*f6aab3d8Srobert }
412*f6aab3d8Srobert 
GetFirstMessageIndex() const413*f6aab3d8Srobert size_t RotatingLogHandler::GetFirstMessageIndex() const {
414*f6aab3d8Srobert   return m_total_count < m_size ? 0 : m_next_index;
415*f6aab3d8Srobert }
416*f6aab3d8Srobert 
Dump(llvm::raw_ostream & stream) const417*f6aab3d8Srobert void RotatingLogHandler::Dump(llvm::raw_ostream &stream) const {
418*f6aab3d8Srobert   std::lock_guard<std::mutex> guard(m_mutex);
419*f6aab3d8Srobert   const size_t start_idx = GetFirstMessageIndex();
420*f6aab3d8Srobert   const size_t stop_idx = start_idx + GetNumMessages();
421*f6aab3d8Srobert   for (size_t i = start_idx; i < stop_idx; ++i) {
422*f6aab3d8Srobert     const size_t idx = NormalizeIndex(i);
423*f6aab3d8Srobert     stream << m_messages[idx];
424*f6aab3d8Srobert   }
425*f6aab3d8Srobert   stream.flush();
426061da546Spatrick }
427