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 // 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(&arg_msg, format, args); 115 va_end(args); 116 117 if (arg_msg != NULL) { 118 static struct timeval g_timeval = {0, 0}; 119 static struct timeval tv; 120 static struct timeval delta; 121 gettimeofday(&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, "%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(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(&arg_msg, format, args); 157 va_end(args); 158 159 if (arg_msg != NULL) { 160 static struct timeval g_timeval = {0, 0}; 161 static struct timeval tv; 162 static struct timeval delta; 163 gettimeofday(&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, "%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(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(&arg_msg, format, args); 197 va_end(args); 198 199 if (arg_msg != NULL) { 200 _DNBLog(DNBLOG_FLAG_ERROR, "error: %s", arg_msg); 201 free(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(&arg_msg, format, args); 214 va_end(args); 215 216 if (arg_msg != NULL) { 217 _DNBLog(DNBLOG_FLAG_ERROR | DNBLOG_FLAG_FATAL, "error: %s", arg_msg); 218 free(arg_msg); 219 } 220 ::exit(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(&arg_msg, format, args); 243 va_end(args); 244 245 if (arg_msg != NULL) { 246 _DNBLog(DNBLOG_FLAG_WARNING | DNBLOG_FLAG_VERBOSE, "warning: %s", 247 arg_msg); 248 free(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(&arg_msg, format, args); 259 va_end(args); 260 261 if (arg_msg != NULL) { 262 _DNBLog(DNBLOG_FLAG_WARNING, "warning: %s", arg_msg); 263 free(arg_msg); 264 } 265 } 266 } 267 268 #endif 269