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
15static int g_debug = 0;
16static 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
29uint32_t g_log_bits = 0;
30static DNBCallbackLog g_log_callback = NULL;
31static void *g_log_baton = NULL;
32
33int DNBLogGetDebug() { return g_debug; }
34
35void DNBLogSetDebug(int g) { g_debug = g; }
36
37int DNBLogGetVerbose() { return g_verbose; }
38
39void DNBLogSetVerbose(int v) { g_verbose = v; }
40
41bool DNBLogCheckLogBit(uint32_t bit) { return (g_log_bits & bit) != 0; }
42
43uint32_t DNBLogSetLogMask(uint32_t mask) {
44 uint32_t old = g_log_bits;
45 g_log_bits = mask;
46 return old;
47}
48
49uint32_t DNBLogGetLogMask() { return g_log_bits; }
50
51void DNBLogSetLogCallback(DNBCallbackLog callback, void *baton) {
52 g_log_callback = callback;
53 g_log_baton = baton;
54}
55
56DNBCallbackLog DNBLogGetLogCallback() { return g_log_callback; }
57
58bool DNBLogEnabled() { return g_log_callback != NULL; }
59
60bool DNBLogEnabledForAny(uint32_t mask) {
61 if (g_log_callback)
62 return (g_log_bits & mask) != 0;
63 return false;
64}
65static 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
74void _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.
83void _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.
94void _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
103static 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.
107void _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.
149void _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.
191void _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.
208void _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.
226void _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.
237void _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.
253void _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

source code of lldb/tools/debugserver/source/DNBLog.cpp