1 //===-- DNBLog.cpp ----------------------------------------------*- C++ -*-===//
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 //  Created by Greg Clayton on 6/18/07.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "DNBLog.h"
14 
15 static int g_debug = 0;
16 static int g_verbose = 0;
17 
18 #if defined(DNBLOG_ENABLED)
19 
20 #include "PThreadMutex.h"
21 #include <mach/mach.h>
22 #include <pthread.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <sys/time.h>
27 #include <unistd.h>
28 
29 uint32_t g_log_bits = 0;
30 static DNBCallbackLog g_log_callback = NULL;
31 static void *g_log_baton = NULL;
32 
33 int DNBLogGetDebug() { return g_debug; }
34 
35 void DNBLogSetDebug(int g) { g_debug = g; }
36 
37 int DNBLogGetVerbose() { return g_verbose; }
38 
39 void DNBLogSetVerbose(int v) { g_verbose = v; }
40 
41 bool DNBLogCheckLogBit(uint32_t bit) { return (g_log_bits & bit) != 0; }
42 
43 uint32_t DNBLogSetLogMask(uint32_t mask) {
44   uint32_t old = g_log_bits;
45   g_log_bits = mask;
46   return old;
47 }
48 
49 uint32_t DNBLogGetLogMask() { return g_log_bits; }
50 
51 void DNBLogSetLogCallback(DNBCallbackLog callback, void *baton) {
52   g_log_callback = callback;
53   g_log_baton = baton;
54 }
55 
56 DNBCallbackLog DNBLogGetLogCallback() { return g_log_callback; }
57 
58 bool DNBLogEnabled() { return g_log_callback != NULL; }
59 
60 bool DNBLogEnabledForAny(uint32_t mask) {
61   if (g_log_callback)
62     return (g_log_bits & mask) != 0;
63   return false;
64 }
65 static inline void _DNBLogVAPrintf(uint32_t flags, const char *format,
66                                    va_list args) {
67   static PThreadMutex g_LogThreadedMutex(PTHREAD_MUTEX_RECURSIVE);
68   PTHREAD_MUTEX_LOCKER(locker, g_LogThreadedMutex);
69 
70   if (g_log_callback)
71     g_log_callback(g_log_baton, flags, format, args);
72 }
73 
74 void _DNBLog(uint32_t flags, const char *format, ...) {
75   va_list args;
76   va_start(args, format);
77   _DNBLogVAPrintf(flags, format, args);
78   va_end(args);
79 }
80 
81 //----------------------------------------------------------------------
82 // Print debug strings if and only if the global g_debug is set to
83 // a non-zero value.
84 //----------------------------------------------------------------------
85 void _DNBLogDebug(const char *format, ...) {
86   if (DNBLogEnabled() && g_debug) {
87     va_list args;
88     va_start(args, format);
89     _DNBLogVAPrintf(DNBLOG_FLAG_DEBUG, format, args);
90     va_end(args);
91   }
92 }
93 
94 //----------------------------------------------------------------------
95 // Print debug strings if and only if the global g_debug is set to
96 // a non-zero value.
97 //----------------------------------------------------------------------
98 void _DNBLogDebugVerbose(const char *format, ...) {
99   if (DNBLogEnabled() && g_debug && g_verbose) {
100     va_list args;
101     va_start(args, format);
102     _DNBLogVAPrintf(DNBLOG_FLAG_DEBUG | DNBLOG_FLAG_VERBOSE, format, args);
103     va_end(args);
104   }
105 }
106 
107 static uint32_t g_message_id = 0;
108 
109 //----------------------------------------------------------------------
110 // Prefix the formatted log string with process and thread IDs and
111 // suffix it with a newline.
112 //----------------------------------------------------------------------
113 void _DNBLogThreaded(const char *format, ...) {
114   if (DNBLogEnabled()) {
115     // PTHREAD_MUTEX_LOCKER(locker, GetLogThreadedMutex());
116 
117     char *arg_msg = NULL;
118     va_list args;
119     va_start(args, format);
120     ::vasprintf(&arg_msg, format, args);
121     va_end(args);
122 
123     if (arg_msg != NULL) {
124       static struct timeval g_timeval = {0, 0};
125       static struct timeval tv;
126       static struct timeval delta;
127       gettimeofday(&tv, NULL);
128       if (g_timeval.tv_sec == 0) {
129         delta.tv_sec = 0;
130         delta.tv_usec = 0;
131       } else {
132         timersub(&tv, &g_timeval, &delta);
133       }
134       g_timeval = tv;
135 
136       // Calling "mach_port_deallocate()" bumps the reference count on the
137       // thread
138       // port, so we need to deallocate it. mach_task_self() doesn't bump the
139       // ref
140       // count.
141       thread_port_t thread_self = mach_thread_self();
142 
143       _DNBLog(DNBLOG_FLAG_THREADED, "%u +%lu.%06u sec [%4.4x/%4.4x]: %s",
144               ++g_message_id, delta.tv_sec, delta.tv_usec, getpid(),
145               thread_self, arg_msg);
146 
147       mach_port_deallocate(mach_task_self(), thread_self);
148       free(arg_msg);
149     }
150   }
151 }
152 
153 //----------------------------------------------------------------------
154 // Prefix the formatted log string with process and thread IDs and
155 // suffix it with a newline.
156 //----------------------------------------------------------------------
157 void _DNBLogThreadedIf(uint32_t log_bit, const char *format, ...) {
158   if (DNBLogEnabled() && (log_bit & g_log_bits) == log_bit) {
159     // PTHREAD_MUTEX_LOCKER(locker, GetLogThreadedMutex());
160 
161     char *arg_msg = NULL;
162     va_list args;
163     va_start(args, format);
164     ::vasprintf(&arg_msg, format, args);
165     va_end(args);
166 
167     if (arg_msg != NULL) {
168       static struct timeval g_timeval = {0, 0};
169       static struct timeval tv;
170       static struct timeval delta;
171       gettimeofday(&tv, NULL);
172       if (g_timeval.tv_sec == 0) {
173         delta.tv_sec = 0;
174         delta.tv_usec = 0;
175       } else {
176         timersub(&tv, &g_timeval, &delta);
177       }
178       g_timeval = tv;
179 
180       // Calling "mach_port_deallocate()" bumps the reference count on the
181       // thread
182       // port, so we need to deallocate it. mach_task_self() doesn't bump the
183       // ref
184       // count.
185       thread_port_t thread_self = mach_thread_self();
186 
187       _DNBLog(DNBLOG_FLAG_THREADED, "%u +%lu.%06u sec [%4.4x/%4.4x]: %s",
188               ++g_message_id, delta.tv_sec, delta.tv_usec, getpid(),
189               thread_self, arg_msg);
190 
191       mach_port_deallocate(mach_task_self(), thread_self);
192 
193       free(arg_msg);
194     }
195   }
196 }
197 
198 //----------------------------------------------------------------------
199 // Printing of errors that are not fatal.
200 //----------------------------------------------------------------------
201 void _DNBLogError(const char *format, ...) {
202   if (DNBLogEnabled()) {
203     char *arg_msg = NULL;
204     va_list args;
205     va_start(args, format);
206     ::vasprintf(&arg_msg, format, args);
207     va_end(args);
208 
209     if (arg_msg != NULL) {
210       _DNBLog(DNBLOG_FLAG_ERROR, "error: %s", arg_msg);
211       free(arg_msg);
212     }
213   }
214 }
215 
216 //----------------------------------------------------------------------
217 // Printing of errors that ARE fatal. Exit with ERR exit code
218 // immediately.
219 //----------------------------------------------------------------------
220 void _DNBLogFatalError(int err, const char *format, ...) {
221   if (DNBLogEnabled()) {
222     char *arg_msg = NULL;
223     va_list args;
224     va_start(args, format);
225     ::vasprintf(&arg_msg, format, args);
226     va_end(args);
227 
228     if (arg_msg != NULL) {
229       _DNBLog(DNBLOG_FLAG_ERROR | DNBLOG_FLAG_FATAL, "error: %s", arg_msg);
230       free(arg_msg);
231     }
232     ::exit(err);
233   }
234 }
235 
236 //----------------------------------------------------------------------
237 // Printing of warnings that are not fatal only if verbose mode is
238 // enabled.
239 //----------------------------------------------------------------------
240 void _DNBLogVerbose(const char *format, ...) {
241   if (DNBLogEnabled() && g_verbose) {
242     va_list args;
243     va_start(args, format);
244     _DNBLogVAPrintf(DNBLOG_FLAG_VERBOSE, format, args);
245     va_end(args);
246   }
247 }
248 
249 //----------------------------------------------------------------------
250 // Printing of warnings that are not fatal only if verbose mode is
251 // enabled.
252 //----------------------------------------------------------------------
253 void _DNBLogWarningVerbose(const char *format, ...) {
254   if (DNBLogEnabled() && g_verbose) {
255     char *arg_msg = NULL;
256     va_list args;
257     va_start(args, format);
258     ::vasprintf(&arg_msg, format, args);
259     va_end(args);
260 
261     if (arg_msg != NULL) {
262       _DNBLog(DNBLOG_FLAG_WARNING | DNBLOG_FLAG_VERBOSE, "warning: %s",
263               arg_msg);
264       free(arg_msg);
265     }
266   }
267 }
268 //----------------------------------------------------------------------
269 // Printing of warnings that are not fatal.
270 //----------------------------------------------------------------------
271 void _DNBLogWarning(const char *format, ...) {
272   if (DNBLogEnabled()) {
273     char *arg_msg = NULL;
274     va_list args;
275     va_start(args, format);
276     ::vasprintf(&arg_msg, format, args);
277     va_end(args);
278 
279     if (arg_msg != NULL) {
280       _DNBLog(DNBLOG_FLAG_WARNING, "warning: %s", arg_msg);
281       free(arg_msg);
282     }
283   }
284 }
285 
286 #endif
287