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 <cstdarg> |
22 | #include <cstdio> |
23 | #include <cstdlib> |
24 | #include <mach/mach.h> |
25 | #include <pthread.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 | // Print debug strings if and only if the global g_debug is set to |
82 | // a non-zero value. |
83 | void _DNBLogDebug(const char *format, ...) { |
84 | if (DNBLogEnabled() && g_debug) { |
85 | va_list args; |
86 | va_start(args, format); |
87 | _DNBLogVAPrintf(DNBLOG_FLAG_DEBUG, format, args); |
88 | va_end(args); |
89 | } |
90 | } |
91 | |
92 | // Print debug strings if and only if the global g_debug is set to |
93 | // a non-zero value. |
94 | void _DNBLogDebugVerbose(const char *format, ...) { |
95 | if (DNBLogEnabled() && g_debug && g_verbose) { |
96 | va_list args; |
97 | va_start(args, format); |
98 | _DNBLogVAPrintf(DNBLOG_FLAG_DEBUG | DNBLOG_FLAG_VERBOSE, format, args); |
99 | va_end(args); |
100 | } |
101 | } |
102 | |
103 | static uint32_t g_message_id = 0; |
104 | |
105 | // Prefix the formatted log string with process and thread IDs and |
106 | // suffix it with a newline. |
107 | void _DNBLogThreaded(const char *format, ...) { |
108 | if (DNBLogEnabled()) { |
109 | // PTHREAD_MUTEX_LOCKER(locker, GetLogThreadedMutex()); |
110 | |
111 | char *arg_msg = NULL; |
112 | va_list args; |
113 | va_start(args, format); |
114 | ::vasprintf(ptr: &arg_msg, f: format, arg: args); |
115 | va_end(args); |
116 | |
117 | if (arg_msg != NULL) { |
118 | static struct timeval g_timeval = {.tv_sec: 0, .tv_usec: 0}; |
119 | static struct timeval tv; |
120 | static struct timeval delta; |
121 | gettimeofday(tv: &tv, NULL); |
122 | if (g_timeval.tv_sec == 0) { |
123 | delta.tv_sec = 0; |
124 | delta.tv_usec = 0; |
125 | } else { |
126 | timersub(&tv, &g_timeval, &delta); |
127 | } |
128 | g_timeval = tv; |
129 | |
130 | // Calling "mach_port_deallocate()" bumps the reference count on the |
131 | // thread |
132 | // port, so we need to deallocate it. mach_task_self() doesn't bump the |
133 | // ref |
134 | // count. |
135 | thread_port_t thread_self = mach_thread_self(); |
136 | |
137 | _DNBLog(DNBLOG_FLAG_THREADED, format: "%u +%lu.%06u sec [%4.4x/%4.4x]: %s" , |
138 | ++g_message_id, delta.tv_sec, delta.tv_usec, getpid(), |
139 | thread_self, arg_msg); |
140 | |
141 | mach_port_deallocate(mach_task_self(), thread_self); |
142 | free(ptr: arg_msg); |
143 | } |
144 | } |
145 | } |
146 | |
147 | // Prefix the formatted log string with process and thread IDs and |
148 | // suffix it with a newline. |
149 | void _DNBLogThreadedIf(uint32_t log_bit, const char *format, ...) { |
150 | if (DNBLogEnabled() && (log_bit & g_log_bits) == log_bit) { |
151 | // PTHREAD_MUTEX_LOCKER(locker, GetLogThreadedMutex()); |
152 | |
153 | char *arg_msg = NULL; |
154 | va_list args; |
155 | va_start(args, format); |
156 | ::vasprintf(ptr: &arg_msg, f: format, arg: args); |
157 | va_end(args); |
158 | |
159 | if (arg_msg != NULL) { |
160 | static struct timeval g_timeval = {.tv_sec: 0, .tv_usec: 0}; |
161 | static struct timeval tv; |
162 | static struct timeval delta; |
163 | gettimeofday(tv: &tv, NULL); |
164 | if (g_timeval.tv_sec == 0) { |
165 | delta.tv_sec = 0; |
166 | delta.tv_usec = 0; |
167 | } else { |
168 | timersub(&tv, &g_timeval, &delta); |
169 | } |
170 | g_timeval = tv; |
171 | |
172 | // Calling "mach_port_deallocate()" bumps the reference count on the |
173 | // thread |
174 | // port, so we need to deallocate it. mach_task_self() doesn't bump the |
175 | // ref |
176 | // count. |
177 | thread_port_t thread_self = mach_thread_self(); |
178 | |
179 | _DNBLog(DNBLOG_FLAG_THREADED, format: "%u +%lu.%06u sec [%4.4x/%4.4x]: %s" , |
180 | ++g_message_id, delta.tv_sec, delta.tv_usec, getpid(), |
181 | thread_self, arg_msg); |
182 | |
183 | mach_port_deallocate(mach_task_self(), thread_self); |
184 | |
185 | free(ptr: arg_msg); |
186 | } |
187 | } |
188 | } |
189 | |
190 | // Printing of errors that are not fatal. |
191 | void _DNBLogError(const char *format, ...) { |
192 | if (DNBLogEnabled()) { |
193 | char *arg_msg = NULL; |
194 | va_list args; |
195 | va_start(args, format); |
196 | ::vasprintf(ptr: &arg_msg, f: format, arg: args); |
197 | va_end(args); |
198 | |
199 | if (arg_msg != NULL) { |
200 | _DNBLog(DNBLOG_FLAG_ERROR, format: "error: %s" , arg_msg); |
201 | free(ptr: arg_msg); |
202 | } |
203 | } |
204 | } |
205 | |
206 | // Printing of errors that ARE fatal. Exit with ERR exit code |
207 | // immediately. |
208 | void _DNBLogFatalError(int err, const char *format, ...) { |
209 | if (DNBLogEnabled()) { |
210 | char *arg_msg = NULL; |
211 | va_list args; |
212 | va_start(args, format); |
213 | ::vasprintf(ptr: &arg_msg, f: format, arg: args); |
214 | va_end(args); |
215 | |
216 | if (arg_msg != NULL) { |
217 | _DNBLog(DNBLOG_FLAG_ERROR | DNBLOG_FLAG_FATAL, format: "error: %s" , arg_msg); |
218 | free(ptr: arg_msg); |
219 | } |
220 | ::exit(status: err); |
221 | } |
222 | } |
223 | |
224 | // Printing of warnings that are not fatal only if verbose mode is |
225 | // enabled. |
226 | void _DNBLogVerbose(const char *format, ...) { |
227 | if (DNBLogEnabled() && g_verbose) { |
228 | va_list args; |
229 | va_start(args, format); |
230 | _DNBLogVAPrintf(DNBLOG_FLAG_VERBOSE, format, args); |
231 | va_end(args); |
232 | } |
233 | } |
234 | |
235 | // Printing of warnings that are not fatal only if verbose mode is |
236 | // enabled. |
237 | void _DNBLogWarningVerbose(const char *format, ...) { |
238 | if (DNBLogEnabled() && g_verbose) { |
239 | char *arg_msg = NULL; |
240 | va_list args; |
241 | va_start(args, format); |
242 | ::vasprintf(ptr: &arg_msg, f: format, arg: args); |
243 | va_end(args); |
244 | |
245 | if (arg_msg != NULL) { |
246 | _DNBLog(DNBLOG_FLAG_WARNING | DNBLOG_FLAG_VERBOSE, format: "warning: %s" , |
247 | arg_msg); |
248 | free(ptr: arg_msg); |
249 | } |
250 | } |
251 | } |
252 | // Printing of warnings that are not fatal. |
253 | void _DNBLogWarning(const char *format, ...) { |
254 | if (DNBLogEnabled()) { |
255 | char *arg_msg = NULL; |
256 | va_list args; |
257 | va_start(args, format); |
258 | ::vasprintf(ptr: &arg_msg, f: format, arg: args); |
259 | va_end(args); |
260 | |
261 | if (arg_msg != NULL) { |
262 | _DNBLog(DNBLOG_FLAG_WARNING, format: "warning: %s" , arg_msg); |
263 | free(ptr: arg_msg); |
264 | } |
265 | } |
266 | } |
267 | |
268 | #endif |
269 | |