SDL_log.c (12087B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "./SDL_internal.h" 22 23#if defined(__WIN32__) || defined(__WINRT__) 24#include "core/windows/SDL_windows.h" 25#endif 26 27/* Simple log messages in SDL */ 28 29#include "SDL_error.h" 30#include "SDL_log.h" 31 32#if HAVE_STDIO_H 33#include <stdio.h> 34#endif 35 36#if defined(__ANDROID__) 37#include <android/log.h> 38#endif 39 40#define DEFAULT_PRIORITY SDL_LOG_PRIORITY_CRITICAL 41#define DEFAULT_ASSERT_PRIORITY SDL_LOG_PRIORITY_WARN 42#define DEFAULT_APPLICATION_PRIORITY SDL_LOG_PRIORITY_INFO 43#define DEFAULT_TEST_PRIORITY SDL_LOG_PRIORITY_VERBOSE 44 45typedef struct SDL_LogLevel 46{ 47 int category; 48 SDL_LogPriority priority; 49 struct SDL_LogLevel *next; 50} SDL_LogLevel; 51 52/* The default log output function */ 53static void SDL_LogOutput(void *userdata, 54 int category, SDL_LogPriority priority, 55 const char *message); 56 57static SDL_LogLevel *SDL_loglevels; 58static SDL_LogPriority SDL_default_priority = DEFAULT_PRIORITY; 59static SDL_LogPriority SDL_assert_priority = DEFAULT_ASSERT_PRIORITY; 60static SDL_LogPriority SDL_application_priority = DEFAULT_APPLICATION_PRIORITY; 61static SDL_LogPriority SDL_test_priority = DEFAULT_TEST_PRIORITY; 62static SDL_LogOutputFunction SDL_log_function = SDL_LogOutput; 63static void *SDL_log_userdata = NULL; 64 65static const char *SDL_priority_prefixes[SDL_NUM_LOG_PRIORITIES] = { 66 NULL, 67 "VERBOSE", 68 "DEBUG", 69 "INFO", 70 "WARN", 71 "ERROR", 72 "CRITICAL" 73}; 74 75#ifdef __ANDROID__ 76static const char *SDL_category_prefixes[SDL_LOG_CATEGORY_RESERVED1] = { 77 "APP", 78 "ERROR", 79 "SYSTEM", 80 "AUDIO", 81 "VIDEO", 82 "RENDER", 83 "INPUT" 84}; 85 86static int SDL_android_priority[SDL_NUM_LOG_PRIORITIES] = { 87 ANDROID_LOG_UNKNOWN, 88 ANDROID_LOG_VERBOSE, 89 ANDROID_LOG_DEBUG, 90 ANDROID_LOG_INFO, 91 ANDROID_LOG_WARN, 92 ANDROID_LOG_ERROR, 93 ANDROID_LOG_FATAL 94}; 95#endif /* __ANDROID__ */ 96 97 98void 99SDL_LogSetAllPriority(SDL_LogPriority priority) 100{ 101 SDL_LogLevel *entry; 102 103 for (entry = SDL_loglevels; entry; entry = entry->next) { 104 entry->priority = priority; 105 } 106 SDL_default_priority = priority; 107 SDL_assert_priority = priority; 108 SDL_application_priority = priority; 109} 110 111void 112SDL_LogSetPriority(int category, SDL_LogPriority priority) 113{ 114 SDL_LogLevel *entry; 115 116 for (entry = SDL_loglevels; entry; entry = entry->next) { 117 if (entry->category == category) { 118 entry->priority = priority; 119 return; 120 } 121 } 122 123 /* Create a new entry */ 124 entry = (SDL_LogLevel *)SDL_malloc(sizeof(*entry)); 125 if (entry) { 126 entry->category = category; 127 entry->priority = priority; 128 entry->next = SDL_loglevels; 129 SDL_loglevels = entry; 130 } 131} 132 133SDL_LogPriority 134SDL_LogGetPriority(int category) 135{ 136 SDL_LogLevel *entry; 137 138 for (entry = SDL_loglevels; entry; entry = entry->next) { 139 if (entry->category == category) { 140 return entry->priority; 141 } 142 } 143 144 if (category == SDL_LOG_CATEGORY_TEST) { 145 return SDL_test_priority; 146 } else if (category == SDL_LOG_CATEGORY_APPLICATION) { 147 return SDL_application_priority; 148 } else if (category == SDL_LOG_CATEGORY_ASSERT) { 149 return SDL_assert_priority; 150 } else { 151 return SDL_default_priority; 152 } 153} 154 155void 156SDL_LogResetPriorities(void) 157{ 158 SDL_LogLevel *entry; 159 160 while (SDL_loglevels) { 161 entry = SDL_loglevels; 162 SDL_loglevels = entry->next; 163 SDL_free(entry); 164 } 165 166 SDL_default_priority = DEFAULT_PRIORITY; 167 SDL_assert_priority = DEFAULT_ASSERT_PRIORITY; 168 SDL_application_priority = DEFAULT_APPLICATION_PRIORITY; 169 SDL_test_priority = DEFAULT_TEST_PRIORITY; 170} 171 172void 173SDL_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 174{ 175 va_list ap; 176 177 va_start(ap, fmt); 178 SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap); 179 va_end(ap); 180} 181 182void 183SDL_LogVerbose(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 184{ 185 va_list ap; 186 187 va_start(ap, fmt); 188 SDL_LogMessageV(category, SDL_LOG_PRIORITY_VERBOSE, fmt, ap); 189 va_end(ap); 190} 191 192void 193SDL_LogDebug(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 194{ 195 va_list ap; 196 197 va_start(ap, fmt); 198 SDL_LogMessageV(category, SDL_LOG_PRIORITY_DEBUG, fmt, ap); 199 va_end(ap); 200} 201 202void 203SDL_LogInfo(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 204{ 205 va_list ap; 206 207 va_start(ap, fmt); 208 SDL_LogMessageV(category, SDL_LOG_PRIORITY_INFO, fmt, ap); 209 va_end(ap); 210} 211 212void 213SDL_LogWarn(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 214{ 215 va_list ap; 216 217 va_start(ap, fmt); 218 SDL_LogMessageV(category, SDL_LOG_PRIORITY_WARN, fmt, ap); 219 va_end(ap); 220} 221 222void 223SDL_LogError(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 224{ 225 va_list ap; 226 227 va_start(ap, fmt); 228 SDL_LogMessageV(category, SDL_LOG_PRIORITY_ERROR, fmt, ap); 229 va_end(ap); 230} 231 232void 233SDL_LogCritical(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 234{ 235 va_list ap; 236 237 va_start(ap, fmt); 238 SDL_LogMessageV(category, SDL_LOG_PRIORITY_CRITICAL, fmt, ap); 239 va_end(ap); 240} 241 242void 243SDL_LogMessage(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) 244{ 245 va_list ap; 246 247 va_start(ap, fmt); 248 SDL_LogMessageV(category, priority, fmt, ap); 249 va_end(ap); 250} 251 252#ifdef __ANDROID__ 253static const char * 254GetCategoryPrefix(int category) 255{ 256 if (category < SDL_LOG_CATEGORY_RESERVED1) { 257 return SDL_category_prefixes[category]; 258 } 259 if (category < SDL_LOG_CATEGORY_CUSTOM) { 260 return "RESERVED"; 261 } 262 return "CUSTOM"; 263} 264#endif /* __ANDROID__ */ 265 266void 267SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va_list ap) 268{ 269 char *message; 270 size_t len; 271 272 /* Nothing to do if we don't have an output function */ 273 if (!SDL_log_function) { 274 return; 275 } 276 277 /* Make sure we don't exceed array bounds */ 278 if ((int)priority < 0 || priority >= SDL_NUM_LOG_PRIORITIES) { 279 return; 280 } 281 282 /* See if we want to do anything with this message */ 283 if (priority < SDL_LogGetPriority(category)) { 284 return; 285 } 286 287 message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE); 288 if (!message) { 289 return; 290 } 291 292 SDL_vsnprintf(message, SDL_MAX_LOG_MESSAGE, fmt, ap); 293 294 /* Chop off final endline. */ 295 len = SDL_strlen(message); 296 if ((len > 0) && (message[len-1] == '\n')) { 297 message[--len] = '\0'; 298 if ((len > 0) && (message[len-1] == '\r')) { /* catch "\r\n", too. */ 299 message[--len] = '\0'; 300 } 301 } 302 303 SDL_log_function(SDL_log_userdata, category, priority, message); 304 SDL_stack_free(message); 305} 306 307#if defined(__WIN32__) 308/* Flag tracking the attachment of the console: 0=unattached, 1=attached, -1=error */ 309static int consoleAttached = 0; 310 311/* Handle to stderr output of console. */ 312static HANDLE stderrHandle = NULL; 313#endif 314 315static void 316SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, 317 const char *message) 318{ 319#if defined(__WIN32__) || defined(__WINRT__) 320 /* Way too many allocations here, urgh */ 321 /* Note: One can't call SDL_SetError here, since that function itself logs. */ 322 { 323 char *output; 324 size_t length; 325 LPTSTR tstr; 326 327#ifndef __WINRT__ 328 BOOL attachResult; 329 DWORD attachError; 330 unsigned long charsWritten; 331 332 /* Maybe attach console and get stderr handle */ 333 if (consoleAttached == 0) { 334 attachResult = AttachConsole(ATTACH_PARENT_PROCESS); 335 if (!attachResult) { 336 attachError = GetLastError(); 337 if (attachError == ERROR_INVALID_HANDLE) { 338 OutputDebugString(TEXT("Parent process has no console\r\n")); 339 consoleAttached = -1; 340 } else if (attachError == ERROR_GEN_FAILURE) { 341 OutputDebugString(TEXT("Could not attach to console of parent process\r\n")); 342 consoleAttached = -1; 343 } else if (attachError == ERROR_ACCESS_DENIED) { 344 /* Already attached */ 345 consoleAttached = 1; 346 } else { 347 OutputDebugString(TEXT("Error attaching console\r\n")); 348 consoleAttached = -1; 349 } 350 } else { 351 /* Newly attached */ 352 consoleAttached = 1; 353 } 354 355 if (consoleAttached == 1) { 356 stderrHandle = GetStdHandle(STD_ERROR_HANDLE); 357 } 358 } 359#endif /* ifndef __WINRT__ */ 360 361 length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1 + 1; 362 output = SDL_stack_alloc(char, length); 363 SDL_snprintf(output, length, "%s: %s\r\n", SDL_priority_prefixes[priority], message); 364 tstr = WIN_UTF8ToString(output); 365 366 /* Output to debugger */ 367 OutputDebugString(tstr); 368 369#ifndef __WINRT__ 370 /* Screen output to stderr, if console was attached. */ 371 if (consoleAttached == 1) { 372 if (!WriteConsole(stderrHandle, tstr, lstrlen(tstr), &charsWritten, NULL)) { 373 OutputDebugString(TEXT("Error calling WriteConsole\r\n")); 374 } 375 if (charsWritten == ERROR_NOT_ENOUGH_MEMORY) { 376 OutputDebugString(TEXT("Insufficient heap memory to write message\r\n")); 377 } 378 } 379#endif /* ifndef __WINRT__ */ 380 381 SDL_free(tstr); 382 SDL_stack_free(output); 383 } 384#elif defined(__ANDROID__) 385 { 386 char tag[32]; 387 388 SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category)); 389 __android_log_write(SDL_android_priority[priority], tag, message); 390 } 391#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA) 392 /* Technically we don't need SDL_VIDEO_DRIVER_COCOA, but that's where this function is defined for now. 393 */ 394 extern void SDL_NSLog(const char *text); 395 { 396 char *text; 397 398 text = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE); 399 if (text) { 400 SDL_snprintf(text, SDL_MAX_LOG_MESSAGE, "%s: %s", SDL_priority_prefixes[priority], message); 401 SDL_NSLog(text); 402 SDL_stack_free(text); 403 return; 404 } 405 } 406#elif defined(__PSP__) 407 { 408 FILE* pFile; 409 pFile = fopen ("SDL_Log.txt", "a"); 410 fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message); 411 fclose (pFile); 412 } 413#endif 414#if HAVE_STDIO_H 415 fprintf(stderr, "%s: %s\n", SDL_priority_prefixes[priority], message); 416#if __NACL__ 417 fflush(stderr); 418#endif 419#endif 420} 421 422void 423SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata) 424{ 425 if (callback) { 426 *callback = SDL_log_function; 427 } 428 if (userdata) { 429 *userdata = SDL_log_userdata; 430 } 431} 432 433void 434SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata) 435{ 436 SDL_log_function = callback; 437 SDL_log_userdata = userdata; 438} 439 440/* vi: set ts=4 sw=4 expandtab: */